본문 바로가기
FireBase

Firebase - 푸시알림 보내기 (2) 앱 실행중 알림 받기

by 봄석 2019. 1. 15.

Firebase - 푸시알림 보내기 (2)

앱을 실행중일때 푸시알람을 받는 방법에대해서 알아보겠습니다.


푸시 알림을 보내기 위해서는 토큰이 있어야 합니다. 

또한 안드로이드 푸시를 사용하기 위해서는 라이브러리가 있어야 합니다.

안드로이드 라이브러리를 build.gradle(module)의 dependencies에 추가합니다.

1
2
 //firebase 푸시알람 라이브러리
    implementation 'com.google.firebase:firebase-messaging:17.3.4'
cs


그리고 푸시아이콘을 drawable에 등록해줍니다(ic_push로 등록)

그리고 manifest에 아래 내용을 추가합니다.



Manifest 설정

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
    .....
    <!-- - 안드로이드 8.0 이상 푸시 처리 추가 코드 -->        
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_icon"
                android:resource="@drawable/ic_push"/>
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_color"
                android:resource="@color/colorAccent"/>
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_channel_id"
                android:value="@string/default_notification_channel_id" />
        <service
                android:name=".fcm.FirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
 
        <service
                android:name=".util.MyJobService"
                android:exported="false">
            <intent-filter>
                <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
            </intent-filter>
        </service>
    </application>
 
</manifest>
 




 <meta-data
          android:name="com.google.firebase.messaging.default_notification_icon"
          android:resource="@drawable/ic_push"/>
<meta-data
          android:name="com.google.firebase.messaging.default_notification_color"
          android:resource="@color/colorAccent"/>

푸시알람이 왔을 때 기본 아이콘 및 색상을 설정하는 메타데이터입니다.






 <meta-data
          android:name="com.google.firebase.messaging.default_notification_channel_id"
          android:value="@string/default_notification_channel_id" />

(Optional) Android 8.0(API 수준 26) 이상부터는 알림 채널이 지원 및 권장됩니다. FCM은 기본적인 설정과 함께 기본 알림 채널을 제공합니다. 기본 채널을 직접 만들어 사용하려면 아래와 같이 default_notification_channel_id를 알림 채널 객체의 ID로 설정합니다. 수신 메시지에 명시적으로 설정된 알림 채널이 없으면 FCM에서 항상 이 값을 사용합니다.








 <service
                android:name=".fcm.FirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>

FirebaseMessagingService는 백그라운드에서 앱의 알람을 수신하는 것 외에 다른 방식으로 메시지를 처리하려는 경우에 필요합니다. 포그라운드 앱의 알림 수신, 데이터 페이로드 수신,

업스트림 메시지 전송 등을 수행하려면 이 서비스를 확장해야 합니다.







<service
                android:name=".util.MyJobService"
                android:exported="false">
            <intent-filter>
                <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
            </intent-filter>
        </service>

이 서비스는 Handler응용 프로그램의 주 스레드 에서 실행중인 각 수신 작업을 실행합니다.
, 실행 논리를 선택한 스레드 / 처리기 / 다른 스레드로 오프로드 해야합니다.

AsyncTask . 그렇게하지 않으면 JobManager에서 향후 콜백을 차단하게됩니다.

구체적으로 onStopJob(android.app.job.JobParameters)일정 요구 사항이 더 이상 충족되지 않는다는 것을 알리기위한 것입니다.



class FirebaseMessagingService : FirebaseMessagingService() {

 
 
    override fun onNewToken(token: String?) { //토큰이 변경되었을때 호출
        super.onNewToken(token)
        //v17.0.0 이후부터는 onTokenRefresh()-depriciated
        //var pushToken = FirebaseInstanceId.getInstance().token
        var uid = FirebaseAuth.getInstance().currentUser!!.uid
        var map = mutableMapOf<String, Any>()
        map["pushtoken"= token!!
        FirebaseFirestore.getInstance().collection("pushtokens").document(uid!!).set(map)
 
        //서버로 바뀐토큰 전송
        sendRegistrationToServer(token)
    }
    private fun sendRegistrationToServer(token: String?) {
        //서버로 바뀐 토큰 전송할 메소드 작성하는 부분
    }
    override fun onMessageReceived(remoteMessage: RemoteMessage?) { //푸시알림을 받았을때 호출되는 메소드
        Log.d(TAG, "From: ${remoteMessage?.from}")
        remoteMessage?.data?.isNotEmpty()?.let {
            Log.d(TAG, "Message data payload: " + remoteMessage.data)
            if (true) {/* 장기 실행 작업으로 데이터를 처리해야하는지 확인*/
                //장기 실행 작업 (10 초 이상)의 경우 Firebase Job Dispatcher를 사용하십시오
                scheduleJob()
            } else {
                //10 초 이내에 메시지 처리
                handleNow()
            }
        }
 
        // 메시지에 알림 페이로드가 포함되어 있는지 확인하십시오.
        remoteMessage?.notification?.let {
            Log.d(TAG, "Message Notification Body: ${it.body}")
            sendNotification(it.body!!)
        }
    }
 
    private fun sendNotification(messageBody: String) {
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this0, intent,
            PendingIntent.FLAG_ONE_SHOT
        )
 
        val channelId = getString(R.string.default_notification_channel_id)
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle("Beomstargram.")
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent)
 
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
 
        // android Oreo 알림 채널이 필요합니다
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "Channel human readable title",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            notificationManager.createNotificationChannel(channel)
        }
 
        notificationManager.notify(0 /*알림ID */, notificationBuilder.build())
    }
 
    private fun scheduleJob() { //장기작업인지(10초이상)일때 처리하는 메소드
        val dispatcher = FirebaseJobDispatcher(GooglePlayDriver(this))
        val myJob = dispatcher.newJobBuilder()
            .setService(MyJobService::class.java)
            .setTag("my-job-tag")
            .build()
        dispatcher.schedule(myJob)
    }
 
    private fun handleNow() {
        Log.d(TAG, "Short lived task is done.")
    }
 
    companion object {
        private val TAG = "FirebaseMessageService"
    }
}



onNewToken() 메소드

override fun onNewToken(token: String?) { //토큰이 변경되었을때 호출

        super.onNewToken(token)

        //v17.0.0 이후부터는 onTokenRefresh()-depriciated

        //var pushToken = FirebaseInstanceId.getInstance().token

        var uid = FirebaseAuth.getInstance().currentUser!!.uid

        var map = mutableMapOf<String, Any>()
        map["pushtoken"] = token!!
        FirebaseFirestore.getInstance().collection("pushtokens").document(uid!!).set(map)
 
        //서버로 바뀐토큰 전송
        sendRegistrationToServer(token)
    }

여기서 FirebaseStore에 토큰을 저장합니다 .

onNewToken메소드는 발급받았던 토큰이 새로 변경될때 호출됩니다.






onMessageRecived() 메소드


override fun onMessageReceived(remoteMessage: RemoteMessage?) { //푸시알림을 받았을때 호출되는 메소드

        Log.d(TAG, "From: ${remoteMessage?.from}")
        remoteMessage?.data?.isNotEmpty()?.let {
            Log.d(TAG, "Message data payload: " + remoteMessage.data)
            if (true) {/* 장기 실행 작업으로 데이터를 처리해야하는지 확인*/
                //장기 실행 작업 (10 초 이상)의 경우 Firebase Job Dispatcher를 사용하십시오
                scheduleJob()
            } else {
                //10 초 이내에 메시지 처리
                handleNow()
            }
        }
 
        // 메시지에 알림 페이로드가 포함되어 있는지 확인하십시오.
        remoteMessage?.notification?.let {
            Log.d(TAG, "Message Notification Body: ${it.body}")
            sendNotification(it.body!!)
        }
    }

푸시알림을 받았을때 호출되는 메소드입니다.

장기작업일때 schedulejob() 메소드를 호출합니다.

10초이내 메시지 처리가 가능할 경우 handleNow() 를 호출합니다.

sendNotification(body)메소드로 메시지의 body를 넘겨줍니다.





sendNotification() 메소드


private fun sendNotification(messageBody: String) {

        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this0, intent,
            PendingIntent.FLAG_ONE_SHOT
        )
 
        val channelId = getString(R.string.default_notification_channel_id)
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle("Beomstargram.")
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent)
 
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
 
        // android Oreo 알림 채널이 필요합니다
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "Channel human readable title",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            notificationManager.createNotificationChannel(channel)
        }
 
        notificationManager.notify(0 /*알림ID */, notificationBuilder.build())
    }

푸시알람을 만들어주는 메소드입니다. 

PendingIntent에 알람을 받을시 넘어갈 화면을 지정합니다.

channelId 는 수신 메시지에 명시적으로 설정된 알림 채널이 없다면 이값이 쓰입니다.

넘겨받은 body로 notificationCompat.Builder로 알림을 만들어줍니다. 






scheduleJob(), handleNow() 메소드

private fun scheduleJob() { //장기작업인지(10초이상)일때 처리하는 메소드
        val dispatcher = FirebaseJobDispatcher(GooglePlayDriver(this))
        val myJob = dispatcher.newJobBuilder()
            .setService(MyJobService::class.java)
            .setTag("my-job-tag")
            .build()
        dispatcher.schedule(myJob)
    }
 
    private fun handleNow() {
        Log.d(TAG, "Short lived task is done.")
    }

장기 작업(10초이상)일때는 scheduleJob() 메소드

그 이하로 처리가능한 작업일때는 handleNow()에 추가적으로 처리할 내용을 작성해줍니다.






MyJobService 클래스 

class MyJobService : JobService() {
    /*
    * 이 서비스는 Handler응용 프로그램의 주 스레드 에서 실행중인 각 수신 작업을 실행합니다.
    * 즉, 실행 논리를 선택한 스레드 / 처리기 / 다른 스레드로 오프로드 해야합니다AsyncTask . 그렇게하지 않으면 JobManager에서 향후 콜백을 차단하게됩니다.
    * 구체적으로 onStopJob(android.app.job.JobParameters)일정 요구 사항이 더 이상 충족되지 않는다는 것을 알리기위한 것입니다.*/
    override fun onStartJob(jobParameters: JobParameters): Boolean {
        //작업이 실행되기 시작했음을 나타 내기 위해 호출됩니다.
        // TODO(developer): add long running task here.
        return false
    }
 
    override fun onStopJob(jobParameters: JobParameters): Boolean {
        return false
        //이 메서드는 호출 기회가 있기 전에도 작업 실행을 중지해야한다고 시스템에서 결정한 경우 호출 jobFinished(JobParameters, boolean)됩니다.
    }
 
    companion object {
 
        private const val TAG = "MyJobService"
    }
}

이 서비스는 Handler응용 프로그램의 주 스레드 에서 실행중인 각 수신 작업을 실행합니다.
, 실행 논리를 선택한 스레드 / 처리기 / 다른 스레드로 오프로드 해야합니다.

AsyncTask . 그렇게하지 않으면 JobManager에서 향후 콜백을 차단하게됩니다.

구체적으로 onStopJob(android.app.job.JobParameters)일정 요구 사항이 더 이상 충족되지 않는다는 것을 알리기위한 것입니다.





MainActivity

class MyJobService : JobService() {

    /*
    * 이 서비스는 Handler응용 프로그램의 주 스레드 에서 실행중인 각 수신 작업을 실행합니다.
    * 즉, 실행 논리를 선택한 스레드 / 처리기 / 다른 스레드로 오프로드 해야합니다AsyncTask . 그렇게하지 않으면 JobManager에서 향후 콜백을 차단하게됩니다.
    * 구체적으로 onStopJob(android.app.job.JobParameters)일정 요구 사항이 더 이상 충족되지 않는다는 것을 알리기위한 것입니다.*/
    override fun onStartJob(jobParameters: JobParameters): Boolean {
        //작업이 실행되기 시작했음을 나타 내기 위해 호출됩니다.
        // TODO(developer): add long running task here.
        return false
    }
 
    override fun onStopJob(jobParameters: JobParameters): Boolean {
        return false
        //이 메서드는 호출 기회가 있기 전에도 작업 실행을 중지해야한다고 시스템에서 결정한 경우 호출 jobFinished(JobParameters, boolean)됩니다.
    }
 
    companion object {
 
        private const val TAG = "MyJobService"
    }
}

MainActivity에서 FirebaseMessaging을 객체를 얻어 초기화 해주면 마지막으로 설정이 끝납니다. subscribeToTopic() 메소드는 사용하면 특정 채널을 구독할 수 있습니다.







https://firebase.google.com/docs/cloud-messaging/android/receive?hl=ko

위 사이트에서 직접 코드도 확인해 볼 수 있습니다

댓글