Reputation: 53
I'm trying to make an app that is able to handle (data-)messages sent by the Azure Notification Hubs. At the current state it sends a Notification when recieving a payload by Azure. While the app is running in the foreground (or still open in the Quick Panel) it has no problems at all and onPushNotificationReceived()
handles the incoming message just fine, but when removing the app from the Quick Panel I get an error for trying to invoke a null object refrence:
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)
This only happens when sending data messages, since the Firebase Service handles Messages with notification payload without invoking onPushNotificationReceived()
.
I've tried the following to fix this:
The first solution resulted in the same error and the second one resulted in no messages at all.
If someone has a way to fix this or knows what could be the fault, I'd be really be happy if you could write an answer :)
Here is the code for both classes (android.app.Service is still included although it didn't work for me). Thanks in advance!
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>
Upvotes: 3
Views: 1251
Reputation: 455
To answer this question, we'll need to discuss some Android lifecycle concepts as documented at https://developer.android.com:
In the code you provide above, you call NotificationHub.setListener
in your application's primary entrypoint, MainActivity
. As you see, this works when your end user has started the application manually because MainActivity.onCreate
gets invoked.
However, there's a second entrypoint in your scenario: FirebaseMessagingService
starting the application when a data-only notification is received in the background. This is subtle and easy to miss - because if the payload contains a notification
component, Android will still route to MainActivity
when the notification is clicked in the system tray.
In this case MainActivity
isn't involved, so MainActivity.onCreate
and NotificationHub.setListener
are never called as the application initialized, and the following line from the stack trace encounters a null reference:
mHub.getInstanceListener().onPushNotificationReceived(this.getApplicationContext(), remoteMessage);
To fix this, you'll need to call NotificationHub.setListener
somewhere that gets called anytime the application gets initialized, regardless of the entrypoint.
The most natural choice is to setup the NotificationHub at the Application level by extending android.app.Application
, overriding the onCreate()
method, and updating your manifest.
Upvotes: 3