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

时间:2019-06-28 13:39:15

标签: android firebase push-notification firebase-cloud-messaging

我有一个配置为通过Firebase接收推送通知的Android应用,并且在手机处于打ze模式时无法正常工作。

应用程序正确接收推送通知,而不管它是在前台还是在后台。为此,我仅使用推送通知中的data字段即可处理任何传入的内容,而与应用程序的状态无关。

我已实现我的服务以接收如下通知:

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)
        }
    }

}

ch属性定义了通知的类型并从我的后端发送:由于我的应用程序具有视频通话功能,因此当有人调用后端时,会使用ch = voip发送通知并进行设置如in the Firebase guide所述,邮件优先级为high

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)
        }
    }

然后,呼叫管理器更新名为currentCall的属性并终止:

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

        status.value = Call.CallStatus.Ringing
    }

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)

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)
    }

此代码显示一条通知,当按下该通知时,将打开IncomingCallActivity并允许用户接受或拒绝该呼叫。此通知还负责使电话响起并振动。

只要在打开屏幕或关闭屏幕的情况下在前台或后台打开应用程序,所有这些功能都可以完美地工作。如果我稍等片刻(从5分钟到几个小时,这取决于时间),一切都会停止工作:从后端发送推送通知时,我的Firebase服务不会被调用(并且我可以看到推送通知已正确发送)。打开屏幕,使来电通知正确显示。

我的日志清楚地表明,直到我打开屏幕,Firebase服务onMessageReceived函数才被调用。

我读过Android Developers - Optimize for Doze and App Standby的一千遍,他们清楚地指出,具有高优先级消息的FCM是在手机处于空闲状态时唤醒应用程序的正确方法。

  

FCM经过优化,可通过高优先级FCM消息与Doze和App Standby空闲模式一起使用。 FCM高优先级消息使您可以可靠地唤醒应用程序以访问网络,即使用户设备处于打ze状态或应用程序处于待机模式也是如此。在“打ze”或“应用程序待机”模式下,系统传递消息并为应用程序提供对网络服务和部分唤醒锁的临时访问权,然后将设备或应用程序返回到空闲状态。

无论如何,这是行不通的。我在不同的手机上尝试过,我可以说在Android 9上,这种行为会更糟,而在Android 7及更低版本上,这种行为并不常见。

我已经看过并尝试了其他question中提出的解决方案,但是它们似乎都不起作用,即使在其中一个答案中,他们说只有在用户刷掉应用程序后才会发生这种情况从多任务处理中,我可以说即使没有强制停止运行该应用程序,它也会发生在我身上。

还有一个问题,更类似于我的问题,here,但所有建议的解决方案均无效。

我不知道如何解决此问题,这是一个非常糟糕的问题,并且会影响我的应用的可用性,因为用户在不使用手机时无法接听视频通话。我还发现其他人在网上报告了此问题,但是所有这些对话似乎都在没有实际解决方案的情况下消失了。

有人可以帮助我吗?

1 个答案:

答案 0 :(得分:1)

事实证明这不是FCM问题,但实际上是Azure Notification Hub(我们的后端用来发送通知的系统)存在的问题。

如果在高优先级消息,Azure Notification Hub和打Hub模式方面遇到问题,请参考此问题的选定答案:Setting Fcm Notification Priority - Azure Notification Hub