适用于 Android 的 Azure 通知中心:如何使用后台服务处理数据消息?

Posted

技术标签:

【中文标题】适用于 Android 的 Azure 通知中心:如何使用后台服务处理数据消息?【英文标题】:Azure Notification Hubs for Android: How do I handle Data-Messages with a Background-Service? 【发布时间】:2021-09-29 20:23:06 【问题描述】:

我正在尝试制作一个能够处理 Azure 通知中心发送的(数据)消息的应用程序。在当前状态下,它会在 Azure 接收有效负载时发送通知。当应用程序在前台运行(或仍在快速面板中打开)时,它完全没有问题,onPushNotificationReceived() 处理传入的消息很好,但是当从快速面板中删除应用程序时,我尝试调用空对象引用:


Logcat
2021-07-22 15:27:33.675 23017-23053/com.example.fcmtutorial1app E/androidRuntime: FATAL EXCEPTION: Firebase-Messaging-Intent-Handle
    Process: com.example.fcmtutorial1app, PID: 23017
    java.lang.NullPointerException: Attempt to invoke interface method 'void com.microsoft.windowsazure.messaging.notificationhubs.NotificationListener.onPushNotificationReceived(android.content.Context, com.google.firebase.messaging.RemoteMessage)' on a null object reference
        at com.microsoft.windowsazure.messaging.notificationhubs.FirebaseReceiver.onMessageReceived(FirebaseReceiver.java:52)
        at com.google.firebase.messaging.FirebaseMessagingService.dispatchMessage(com.google.firebase:firebase-messaging@@22.0.0:13)
        at com.google.firebase.messaging.FirebaseMessagingService.passMessageIntentToSdk(com.google.firebase:firebase-messaging@@22.0.0:8)
        at com.google.firebase.messaging.FirebaseMessagingService.handleMessageIntent(com.google.firebase:firebase-messaging@@22.0.0:3)
        at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(com.google.firebase:firebase-messaging@@22.0.0:3)
        at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(com.google.firebase:firebase-messaging@@22.0.0:1)
        at com.google.firebase.messaging.EnhancedIntentService$$Lambda$0.run(Unknown Source:6)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at com.google.android.gms.common.util.concurrent.zza.run(Unknown Source:6)
        at java.lang.Thread.run(Thread.java:923)

这仅在发送数据消息时发生,因为 Firebase 服务处理带有通知负载的消息而不调用 onPushNotificationReceived()


我已尝试以下方法来解决此问题:

使用 android.app.Service 扩展 CustomNotificationListener.class 将 onPushNotificationReceived() 替换为 Thunderbirds onMessageReceived()

第一个解决方案导致相同的错误,而第二个解决方案根本没有任何消息。

如果有人有办法解决这个问题或知道可能是什么问题,如果你能写一个答案,我会非常高兴:)

这是两个类的代码(android.app.Service 仍然包括在内,尽管它对我不起作用)。提前致谢!


MainActivity.class
package com.example.fcmtutorial1app;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.microsoft.windowsazure.messaging.notificationhubs.NotificationHub;

public class MainActivity extends AppCompatActivity

    public static final String CHANNEL_1_ID = "Channel1";
    public static final String CHANNEL_2_ID = "Channel2";

    public static String editTextTitle;
    public static String editTextMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        createNotificationsChannels();

        NotificationHub.setListener(new CustomNotificationListener());
        NotificationHub.start(this.getApplication(), "spfcmtutorial1nhub", "Endpoint=sb://azurecloudmessaging.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=abc[...]xyz");
    

    public static void sendCloudMessage(Context context)
    
        editTextTitle = CustomNotificationListener.title;
        editTextMessage = CustomNotificationListener.body;

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_1_ID)
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle(editTextTitle)
                .setContentText(editTextMessage)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_MESSAGE);

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(1, notificationBuilder.build());

        Log.v("MSG", "SENDCLOUDMESSAGE WAS ACTIVATED");
    

    public void createNotificationsChannels() //Channel 2 is for tests only
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        
            NotificationChannel channel1 = new NotificationChannel(
                    CHANNEL_1_ID,
                    "Channel 1",
                    NotificationManager.IMPORTANCE_HIGH
            );
            channel1.setDescription("This is Channel 1");

            NotificationChannel channel2 = new NotificationChannel(
                    CHANNEL_2_ID,
                    "Channel 2",
                    NotificationManager.IMPORTANCE_LOW
            );
            channel2.setDescription("This is Channel 2");

            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel1);
            manager.createNotificationChannel(channel2);
        
    


CustomNotificationListener.class
package com.example.fcmtutorial1app;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import com.google.firebase.messaging.RemoteMessage;
import com.microsoft.windowsazure.messaging.notificationhubs.NotificationListener;
import java.util.Map;

public class CustomNotificationListener extends Service implements NotificationListener

    private static final String TAG = "Message";

    public static String title;
    public static String body;
    public static String dataTitle;
    public static String dataBody;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    
        Log.d(TAG, "Service started");
        return Service.START_NOT_STICKY;
    

    @Override
    public void onPushNotificationReceived(Context context, RemoteMessage message) //FATAL EXEPTION: Firebase-Messaging-Intent-Handle HERE
    
        RemoteMessage.Notification notification = message.getNotification();

        try  title = notification.getTitle();  catch(Exception e) 
        try  body = notification.getBody();  catch (Exception e) 

        Map<String, String> data = message.getData();

        //region LOGGING
        if (message != null)
        
            Log.d(TAG, "Message Notification Title: " + title);
            Log.d(TAG, "Message Notification Body: " + body);
        
        else  Log.e(TAG, "ERROR, no message found"); 

        if (data != null)
        
            for (Map.Entry<String, String> entry : data.entrySet())
            
                Log.d(TAG, "key, " + entry.getKey() + "value " + entry.getValue());
            
        
        else  Log.e(TAG, "ERROR, no data found"); 
        //endregion

        Log.v("VERBOSE",  data.get("property1"));

        MainActivity.sendCloudMessage(context);
    

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return null;
    


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fcmtutorial1app">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.FCMTutorial1App">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".CustomNotificationListener"></service>
    </application>

</manifest>

【问题讨论】:

【参考方案1】:

要回答这个问题,我们需要讨论https://developer.android.com 中记录的一些 Android 生命周期概念:

Process and Application Lifecycle Activity Lifecycle

在上面提供的代码中,您在应用程序的主入口点MainActivity 中调用NotificationHub.setListener。如您所见,这在您的最终用户手动启动应用程序时有效,因为 MainActivity.onCreate 被调用。

但是,您的场景中有第二个入口点:FirebaseMessagingService 在后台收到纯数据通知时启动应用程序。这是微妙且容易错过的——因为如果有效负载包含notification 组件,当在系统托盘中单击通知时,Android 仍会路由到MainActivity

在这种情况下,MainActivity 不涉及,所以 MainActivity.onCreateNotificationHub.setListener 在应用程序初始化时永远不会被调用,following line from the stack trace 遇到空引用:

mHub.getInstanceListener().onPushNotificationReceived(this.getApplicationContext(), remoteMessage);

要解决此问题,无论入口点如何,您都需要在应用程序初始化时调用的某个地方调用 NotificationHub.setListener

最自然的选择是通过扩展 android.app.Application、覆盖 onCreate() method 和 updating your manifest 在应用程序级别设置 NotificationHub。

【讨论】:

谢谢!创建一个新类并对其进行扩展非常有效^^ 我很高兴!谢谢你告诉我。 @MarStr 我按照您说的进行了配置,现在可以使用。但这会导致另一个问题。 + I use azure notification hub for android sdk v1.1.6 + I use firebase Data message + I set azure listener on Application onCreate() 但是当我关闭手机屏幕大约15-20分钟时,我发送了一条数据消息,并且没有推送到我的手机。我认为在使用数据消息时,Azure 集线器需要保留一个服务来监听 Firebase 推送,但是当手机离屏时间过长时,该服务会被终止。我说得对吗,@MarStr?请帮我检查一下。非常感谢。

以上是关于适用于 Android 的 Azure 通知中心:如何使用后台服务处理数据消息?的主要内容,如果未能解决你的问题,请参考以下文章

Azure 通知中心:SendTemplateNotificationAsync 是不是适用于 .net Core

Azure 通知适用于移动数据而不是 Wi-Fi

将数据负载添加到 Xamarin 推送通知(GFB 和 Azure 通知中心)

适用于 chrome、firefox 和 safari 浏览器的 Azure 浏览器推送通知

适用于 Android 谷歌云消息 (GCM) 的 Azure 移动服务从不向设备发送通知

推送天蓝色通知中心的通知