从后端发送自定义推送通知。有效负载如下
Payload
{
"data": {
"notification": {
"title": "কথা চলুক সেরা অফারে!",
"body": "১৯৯ টাকায় ৩০০ মিনিট ৩০ দিন মেয়াদে পেতে ট্যাপ করুন!",
"imageUrl": "https://myapp-dev-static.robi.com.bd/images/payment/bkash.png"
},
"data": {
"title": "কথা চলুক সেরা অফারে!",
"description": "১৯৯ টাকায় ৩০০ মিনিট ৩০ দিন মেয়াদে পেতে ট্যাপ করুন!",
"sentAt": "1731929876", //
"expiredAt": "1731982676",
"buttonText": "Open Recharge",
"type": "in_app",
"url": "myapp-ppd://dashboard",
"image": "https://media.giphy.com/media/Ju7l5y9osyymQ/giphy.gif"
},
"token": "XXX"
},
"operator": "moon"
}
我要做的是将通知数据保存到我的房间数据库中。这是实现,我尝试过
SaveNotificationWorker.kt
@HiltWorker
class SaveNotificationWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
@Inject
lateinit var notificationRepository: NotificationLocalRepository
override suspend fun doWork(): Result {
val jsonString = inputData.getString("data")
if (jsonString.isNullOrEmpty()) return Result.failure()
return try {
val notificationEntity = Gson().fromJson(jsonString, NotificationEntity::class.java)
notificationRepository.addNotification(notificationEntity)
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
RasaFcmService.kt
@AndroidEntryPoint
class RasaFcmService : FirebaseMessagingService() {
@Inject
lateinit var notificationRepository: NotificationLocalRepository
override fun onNewToken(token: String) {
super.onNewToken(token)
Log.d("fcm", token)
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
val notificationEntity = parseData(remoteMessage.data)
notificationEntity?.let {
val jsonString = Gson().toJson(notificationEntity)
val workRequest = OneTimeWorkRequestBuilder<SaveNotificationWorker>()
.setInputData(workDataOf("data" to jsonString))
.build()
WorkManager.getInstance(applicationContext).enqueue(workRequest)
showNotification(it.id, it.title, it.description)
}
}
private fun parseData(data: Map<String, String>): NotificationEntity? {
return try {
val notificationJson = JSONObject(data)
val title = notificationJson.optString("title")
val description = notificationJson.optString("description")
val sentAt = notificationJson.optLong("sentAt", System.currentTimeMillis() / 1000)
val linkType = if (notificationJson.has("type")) notificationJson.optString("type") else null
val link = if (notificationJson.has("url")) notificationJson.optString("url") else null
val image = if (notificationJson.has("image")) notificationJson.optString("image") else null
val expiredAt = if (notificationJson.has("expiredAt")) notificationJson.optString("expiredAt") else null
val buttonText = notificationJson.optString("buttonText")
NotificationEntity(
title = title,
description = description,
date = sentAt,
isRead = 0,
expiredAt = expiredAt?.toLong(),
buttonText = buttonText,
linkType = linkType,
link = link,
image = image
)
} catch (e: Exception) {
null
}
}
private fun showNotification(id: Int, title: String, description: String) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "YOUR_CHANNEL_ID"
val channelName = "Your Channel Name"
val intent = Intent(this, SplashActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra("notification_id", id)
}
val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, channelId)
.setContentTitle(title)
.setContentText(description)
.setSmallIcon(R.drawable.ic_brand_logo)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.build()
notificationManager.notify(id, notification)
}
}
在此实现中,当应用程序位于前台时,数据会完美保存在 Room 中。但是,当应用程序处于后台或不处于活动状态时,即使应用程序处于后台,我也想将其保存在 Room 中,但不会保存数据。如何解决这个问题?
对于后台任务,请使用旧方法,不要使用 HILT。试试这个
onMessageReceived
//initialize database
//after using the below approach
CoroutineScope(Dispatchers.IO).launch {
//save to database code
}
为什么应用程序在后台时推送通知数据没有保存到房间
使用 Firebase Cloud Messaging (FCM) 时,通知的行为取决于有效负载的结构。 FCM 允许两种类型的有效负载:通知有效负载和数据有效负载。了解他们的行为对于确保您的应用正确处理通知和保存数据至关重要。
当有效负载包含通知对象时,FCM 会自动处理通知显示。这意味着:
• The notification is shown in the system tray by FCM without involving your app.
• The FirebaseMessagingService’s onMessageReceived() method is not triggered if the app is in the background or terminated.
• You cannot directly process the notification payload to save it to the Room database in these cases.
当有效负载仅包含数据对象时:
• The onMessageReceived() method is triggered in all app states (foreground, background, or terminated).
• You have full control to process the data and handle tasks like saving it to Room, displaying a custom notification, etc.
enter code here
如果有效负载同时包含通知和数据对象:
• FCM prioritizes the notification object for background and terminated states, displaying it automatically in the system tray.
• The data object is ignored, and onMessageReceived() is not called.
解决方案
有效处理并保存推送通知数据到Room:
• Use only the data payload in your backend’s FCM request. Avoid including the notification object.
• Construct your custom notification in the onMessageReceived() method, allowing you to display the notification and save its content to Room simultaneously.
示例有效负载
通知负载(不推荐):
{
"notification": {
"title": "New Offer!",
"body": "Click to check out the latest deals."
},
"data": {
"type": "offer",
"url": "https://example.com/deals"
}
}
• 后台行为:自动显示通知,不触发 onMessageReceived()。
数据负载(推荐):
{
"data": {
"title": "New Offer!",
"body": "Click to check out the latest deals.",
"type": "offer",
"url": "https://example.com/deals"
}
}
• 后台行为:onMessageReceived() 被触发,让您完全控制。
后端实现
确保您的后端仅发送数据对象,以允许在所有应用程序状态下一致地处理通知。这提供了自定义通知和处理任务(例如将数据保存到 Room)的灵活性。