简体   繁体   English

使用 Firebase 处理后台推送通知,支持 Doze

[英]Handle background push notifications with Firebase, supporting Doze

I have an Android app configured to receive push notifications through Firebase and I'm having issues making it work while the phone is in Doze mode.我有一个 Android 应用程序配置为通过 Firebase 接收推送通知,但在手机处于打盹模式时无法正常工作。

The application correctly receives push notifications, regardless if it is in foreground or in background.应用程序正确接收推送通知,无论它是在前台还是在后台。 To do this, I am using only the data field in the push notification to be able to handle anything that comes in regardless of the status of the app.为此,我仅使用推送通知中的data字段来处理任何传入的内容,而不管应用程序的状态如何。

I have implemented my service to receive notifications as follows:我已经实现了我的服务来接收通知,如下所示:

class MyFirebaseMessagingService : FirebaseMessagingService() {

   override fun onMessageReceived(p0: RemoteMessage?) {
        Timber.d("Push notification received")
        super.onMessageReceived(p0)
        when (p0!!.data["ch"]) {
            NotificationType.VoIP.channelType() -> handleVoIPNotification(p0.data)
            NotificationType.Push.channelType() -> handlePushNotification(p0.data)
        }
    }

}

The ch property defines the type of notification and is sent from my backend: since my app has a video-calling function, when someone is calling the backend sends out a notification with ch = voip and sets the message priority to high , as documented in the Firebase guide . ch属性定义了通知的类型并从我的后端发送:由于我的应用程序具有视频通话功能,当有人呼叫后端时,会发送一个带有ch = voip的通知并将消息优先级设置为high ,如中所述Firebase 指南

The handleVoIPNotification function contains this: handleVoIPNotification函数包含以下内容:

private fun handleVoIPNotification(data: Map<String, String>) {
        val gson = Gson()
        val jsonElement = gson.toJsonTree(data)

        try {
            val voIPNotification = gson.fromJson(jsonElement, VoIPNotification::class.java)

            Timber.i("VoIP Notification received: %s", voIPNotification.action.name)

            // pass the incoming call data to the Call Manager.
            CallManager.getInstance().handleNotification(voIPNotification)
        } catch (exc: Exception) {
            Timber.e(exc)
            Timber.i("Invalid VoIP notification received: %s", data)
        }
    }

The Call Manager then updates a property named currentCall and terminates:然后呼叫管理器更新名为currentCall的属性并终止:

this.currentCall.apply {
        token = notification.token
        roomName = notification.room
        username = notification.nickname

        status.value = Call.CallStatus.Ringing
    }

The status property is a BehaviorSubject implementation that is observed by another object that reacts to changes of the status of the call: status属性是一个BehaviorSubject实现,它被另一个对象观察到,该对象对调用状态的变化做出反应:

currentCall.status.observable
                .distinctUntilChanged()
                .subscribe {
                    Timber.d("Call status changed: $it")

                    when (it) {
                        Call.CallStatus.Ringing -> {
                            this.showIncomingCallNotification()
                        }
                        Call.CallStatus.Declined, Call.CallStatus.Ended -> {
                            this.dismissIncomingCallNotification()
                            this.refreshIncomingCallActivity()
                        }
                        Call.CallStatus.Connecting -> {
                            this.dismissIncomingCallNotification()
                            this.presentOngoingCallActivity()
                        }
                        else -> { /* ignored */ }
                    }
                }.disposedBy(this.disposeBag)

The showIncomingCallNotification is the following: showIncomingCallNotification如下:

fun showIncomingCallNotification() {
        val intent = Intent(Intent.ACTION_MAIN, null).apply {
            flags = Intent.FLAG_ACTIVITY_NO_USER_ACTION or Intent.FLAG_ACTIVITY_NEW_TASK
            setClass(configuration.context, configuration.incomingCallActivityType.java)
        }

        val pendingIntent = PendingIntent.getActivity(configuration.context, configuration.requestCode, intent, 0)

        val builder = NotificationCompat.Builder(configuration.context, configuration.notificationChannel)
                .setOngoing(true)
                .setContentIntent(pendingIntent)
                .setFullScreenIntent(pendingIntent, true)
                .setSmallIcon(configuration.notificationIcon)
                .setContentTitle(currentCall.username)
                .setContentText(configuration.context.getString(configuration.notificationText))

        val notification = builder.build()
        notification.flags = notification.flags or Notification.FLAG_INSISTENT

        configuration.notificationsManager.getSystemNotificationManager().notify(0, notification)
    }

This code shows a notification which, when pressed, opens the IncomingCallActivity and lets the user accept or reject the call.此代码显示一个通知,当按下该通知时,将打开 IncomingCallActivity 并让用户接受或拒绝呼叫。 This notification is also responsible of making the phone ring and vibrate.此通知还负责使电话响铃和振动。

All of this works perfectly when the app is opened in foreground or in the background, as long as the screen is on or it has just been turned off.当应用程序在前台或后台打开时,只要屏幕打开或刚刚关闭,所有这些都可以完美运行。 If I wait a while (from 5 minutes to hours, it depends), everything stops working: my Firebase service is not called when a push notification is sent from my backend (and I can see that the push notification has been correctly sent).如果我等待一段时间(从 5 分钟到几小时,视情况而定),一切都会停止工作:当我的后端发送推送通知时,我的 Firebase 服务不会被调用(我可以看到推送通知已正确发送)。 Turning on the screen, makes the incoming call notification appear correctly.打开屏幕,使来电通知正确显示。

I have logs in place that clearly show that the Firebase service onMessageReceived function is not even called until I turn on the screen.我的日志清楚地表明,在我打开屏幕之前,甚至没有调用 Firebase 服务onMessageReceived函数。

I have read a thousand times the Android Developers - Optimize for Doze and App Standby and they clearly state that FCM with High priority messages is the correct approach for waking up an app when the phone is in idle.我已经阅读了一千遍Android Developers - Optimize for Doze 和 App Standby ,他们清楚地指出带有高优先级消息的 FCM 是在手机空闲时唤醒应用程序的正确方法。

FCM is optimized to work with Doze and App Standby idle modes by means of high-priority FCM messages. FCM 经过优化,可通过高优先级 FCM 消息在 Doze 和 App Standby 空闲模式下工作。 FCM high-priority messages let you reliably wake your app to access the network, even if the user's device is in Doze or the app is in App Standby mode. FCM 高优先级消息可让您可靠地唤醒应用程序以访问网络,即使用户的设备处于 Doze 或应用程序处于应用程序待机模式也是如此。 In Doze or App Standby mode, the system delivers the message and gives the app temporary access to network services and partial wakelocks, then returns the device or app to the idle state.在 Doze 或 App Standby 模式下,系统传递消息并让应用程序临时访问网络服务和部分唤醒锁,然后将设备或应用程序返回到空闲状态。

Regardless, this doesn't work.无论如何,这是行不通的。 I have tried on different phones and I can say that the behavior is way worse on Android 9, whereas from Android 7 and lower it is not so common.我在不同的手机上尝试过,我可以说这种行为在 Android 9 上更糟糕,而在 Android 7 及更低版本上,这种情况并不常见。

I have seen and tried the solutions proposed in this other question , but none of them seems to work and, even if in one of the answers they say that this happens only if the user has swiped away the app from multi-tasking, I can say that it happens to me even if the app hasn't been force stopped.我已经看到并尝试了在另一个问题中提出的解决方案,但它们似乎都不起作用,即使在其中一个答案中他们说只有当用户从多任务处理中刷掉应用程序时才会发生这种情况,我也可以说即使应用程序没有被强制停止,它也会发生在我身上。

There's also another question, more similar to my issue, here but all the proposed solutions don't work.还有另一个问题,更类似于我的问题, 这里但所有提出的解决方案都不起作用。

I have no idea how to solve this and it is a very bad issue and affects the usability of my app, since the user is unable to answer to video-calls when they're not using their phone.我不知道如何解决这个问题,这是一个非常糟糕的问题,会影响我的应用程序的可用性,因为用户在不使用手机时无法接听视频通话。 I also found other people online reporting this issue, but all of those conversations seem to die at some point without an actual solution…我还发现其他人在网上报告了这个问题,但所有这些对话似乎都在某个时候没有实际解决方案就消失了……

Can anyone help me?谁能帮我?

Turns out that this is not a FCM issue, but it's actually a problem with Azure Notification Hub (which is the system our backend is using to send out notifications).事实证明,这不是 FCM 问题,但实际上是 Azure 通知中心(我们的后端用于发送通知的系统)的问题。

If you're having issues with High Priority messages, Azure Notification Hub and Doze Mode, refer to the chosen answer of this question: Setting Fcm Notification Priority - Azure Notification Hub如果您在使用高优先级消息、Azure 通知中心和打盹模式方面遇到问题,请参阅此问题的所选答案: 设置 Fcm 通知优先级 - Azure 通知中心

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM