Firebase Cloud Messaging
Firebase Cloud Messaging 이하 FCM은 메시지를 빌드, 전송, 수신하는 크로스 플랫폼 메시징 솔루션이다. 안드로이드 앱에서 흔하게 볼 수 있는 Push 메시지가 대표적인 사용 예시이다. 동작 방식은 아래 도표와 같다. Firebase 콘솔 GUI 혹은 써드 파티 서버(node, php, ...) 등에서 메시지를 전송하도록 FCM 서버에 요청하면, FCM 서버에서 이를 각 플랫폼에 맞게 적절히 메시지를 전달한다. 여기서 말하는 메시지란, 단순히 유저로에게 어떠한 알림을 보내는 것 이외에도 디테일한 데이터 전달 또한 가능하다. 이러한 데이터는 보통 json, xml로 전송된다.
안드로이드 Client 측에서의 세팅
1. Firebase SDK 추가
아주 기본적인 Firebase 구성요소 세팅은 콘솔에서 프로젝트를 생성할 때 다 안내되므로 생략한다. 먼저 app단위의 build:gradle에서 다음 구문을 추가하여 FCM SDK를 설치한다.
dependencies {
// ...
// Add the dependencies for the Firebase Cloud Messaging and Analytics libraries
implementation 'com.google.firebase:firebase-messaging:20.2.4'
implementation 'com.google.firebase:firebase-analytics:17.5.0'
}
2. manifest.xml 수정
FirebaseMessagingService를 상속받은 서비스 컴포넌트를 활용하기 위해 아래처럼 추가해준다. 이러한 과정은 클라이언트단에서 메시지를 받아 notification을 유저에게 발생시키거나 데이터를 처리하기 위해서 꼭 필요하다.
<service
android:name=".java.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
3. Token 정보 획득
초기에 앱이 시작될 때 FCM SDK에서는 registeration token을 생성한다. 이 토큰은 디바이스를 식별하기 위해 사용된다. 그리고 앱이 설치/재설치/앱 데이터 삭제 시에 token 정보는 바뀔 수 있다. 그렇기에 항상 token 정보를 최신의 상태로 유지하여야 한다.
-token 획득 방법
FirebaseInstanceId.getInstance().instanceId
.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "getInstanceId failed", task.exception)
return@OnCompleteListener
}
// Get new Instance ID token
val token = task.result?.token
// Log and toast
val msg = getString(R.string.msg_token_fmt, token)
Log.d(TAG, msg)
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
})
- token의 변화를 감지하는 방법
FirebaseMessagingService를 상속받으면 아래 onNewToken 메서드를 오버라이딩 해야된다. 여기서 토큰 정보가 새로 업데이트 되거나 추가되면 토큰 정보를 얻어올 수 있다. 이렇게 얻어온 정보는 firestore나 자체적으로 구현한 서버에 전송할 수 있다.
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
//토큰 정보를 백엔드 서버로 전송하기
sendRegistrationToServer(token)
}
4. 메시지 수신
아래는 Firebase 기본 샘플 예시이다. 추가적으로 notification을 생성하는 함수가 구현되어있다. 메시지를 수신하면 onMessageReceived 함수가 호출되어 전달받은 데이터를 얻어올 수 있다.
package com.google.firebase.quickstart.fcm.kotlin
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.google.firebase.quickstart.fcm.R
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.d(TAG, "From: ${remoteMessage.from}")
if (remoteMessage.data.isNotEmpty()) {
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
}
}
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
sendRegistrationToServer(token)
}
private fun sendRegistrationToServer(token: String?) {
// TODO: Implement this method to send token to your app server.
Log.d(TAG, "sendRegistrationTokenToServer($token)")
}
/**
* Create and show a simple notification containing the received FCM message.
*
* @param messageBody FCM message body received.
*/
private fun sendNotification(messageBody: String) {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, 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.drawable.ic_stat_ic_notification)
.setContentTitle(getString(R.string.fcm_message))
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
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 of notification */, notificationBuilder.build())
}
companion object {
private const val TAG = "MyFirebaseMsgService"
}
}
'안드로이드' 카테고리의 다른 글
[안드로이드] 서비스(Service) (1) | 2020.09.10 |
---|---|
[안드로이드] 브로드캐스트 리시버 (0) | 2020.09.09 |
[안드로이드] 예제: MVVM+AAC를 이용한 RecyclerView 10 - Swipe, Drag&Drop으로 아이템 삭제 및 순서 변경 버그 픽스 (0) | 2020.08.24 |
[안드로이드] 예제: MVVM+AAC를 이용한 RecyclerView 9 - Swipe, Drag&Drop으로 아이템 삭제 및 순서 변경 (0) | 2020.08.24 |
[안드로이드] 예제: MVVM+AAC를 이용한 RecyclerView 8 - Navigation 컴포넌트 활용 (0) | 2020.08.21 |