diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java index f9e10df1bd1c..ef5686fbf32f 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java @@ -11,11 +11,19 @@ import android.util.Log; import com.google.firebase.messaging.RemoteMessage; import java.util.HashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; public class FlutterFirebaseMessagingReceiver extends BroadcastReceiver { private static final String TAG = "FLTFireMsgReceiver"; static HashMap notifications = new HashMap<>(); + // SharedPreferences writes and process-state lookups must not run on the main + // thread: when a receiver returns, Android blocks the main thread until all + // pending SharedPreferences.apply() writes have hit disk (QueuedWork.waitToFinish), + // which causes ANRs under I/O pressure. + private static final Executor backgroundExecutor = Executors.newSingleThreadExecutor(); + @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "broadcast received for message"); @@ -38,37 +46,53 @@ public void onReceive(Context context, Intent intent) { RemoteMessage remoteMessage = new RemoteMessage(intent.getExtras()); - // Store the RemoteMessage if the message contains a notification payload. + // Keep the in-memory message available to the plugin on the main thread; it is + // read from the main thread when a notification is tapped. if (remoteMessage.getNotification() != null) { notifications.put(remoteMessage.getMessageId(), remoteMessage); - FlutterFirebaseMessagingStore.getInstance().storeFirebaseMessage(remoteMessage); } - // |-> --------------------- - // App in Foreground - // ------------------------ - if (FlutterFirebaseMessagingUtils.isApplicationForeground(context)) { - FlutterFirebaseRemoteMessageLiveData.getInstance().postRemoteMessage(remoteMessage); - return; - } + // goAsync() keeps the broadcast alive until pendingResult.finish() so the + // remaining work can safely run off the main thread. + final PendingResult pendingResult = goAsync(); + backgroundExecutor.execute( + () -> { + try { + // Store the RemoteMessage if the message contains a notification payload. + if (remoteMessage.getNotification() != null) { + FlutterFirebaseMessagingStore.getInstance().storeFirebaseMessage(remoteMessage); + } + + // |-> --------------------- + // App in Foreground + // ------------------------ + if (FlutterFirebaseMessagingUtils.isApplicationForeground(context)) { + FlutterFirebaseRemoteMessageLiveData.getInstance().postRemoteMessage(remoteMessage); + return; + } - // |-> --------------------- - // App in Background/Quit - // ------------------------ - Intent onBackgroundMessageIntent = - new Intent(context, FlutterFirebaseMessagingBackgroundService.class); + // |-> --------------------- + // App in Background/Quit + // ------------------------ + Intent onBackgroundMessageIntent = + new Intent(context, FlutterFirebaseMessagingBackgroundService.class); - Parcel parcel = Parcel.obtain(); - remoteMessage.writeToParcel(parcel, 0); - // We write to parcel using RemoteMessage.writeToParcel() to pass entire RemoteMessage as array - // of bytes - // Which can be read using RemoteMessage.createFromParcel(parcel) API - onBackgroundMessageIntent.putExtra( - FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, parcel.marshall()); + Parcel parcel = Parcel.obtain(); + remoteMessage.writeToParcel(parcel, 0); + // We write to parcel using RemoteMessage.writeToParcel() to pass entire RemoteMessage + // as array + // of bytes + // Which can be read using RemoteMessage.createFromParcel(parcel) API + onBackgroundMessageIntent.putExtra( + FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, parcel.marshall()); - FlutterFirebaseMessagingBackgroundService.enqueueMessageProcessing( - context, - onBackgroundMessageIntent, - remoteMessage.getOriginalPriority() == RemoteMessage.PRIORITY_HIGH); + FlutterFirebaseMessagingBackgroundService.enqueueMessageProcessing( + context, + onBackgroundMessageIntent, + remoteMessage.getOriginalPriority() == RemoteMessage.PRIORITY_HIGH); + } finally { + pendingResult.finish(); + } + }); } }