Skip to content

[firebase_messaging] better handling on background isolate initialization #1917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,7 @@ private void onAttachedToEngine(Context context, BinaryMessenger binaryMessenger
this.applicationContext = context;
FirebaseApp.initializeApp(applicationContext);
channel = new MethodChannel(binaryMessenger, "plugins.flutter.io/firebase_messaging");
final MethodChannel backgroundCallbackChannel =
new MethodChannel(binaryMessenger, "plugins.flutter.io/firebase_messaging_background");

channel.setMethodCallHandler(this);
backgroundCallbackChannel.setMethodCallHandler(this);
FlutterFirebaseMessagingService.setBackgroundChannel(backgroundCallbackChannel);

// Register broadcast receiver
IntentFilter intentFilter = new IntentFilter();
Expand Down Expand Up @@ -174,12 +169,10 @@ public void onMethodCall(final MethodCall call, final Result result) {
e.printStackTrace();
}
FlutterFirebaseMessagingService.setBackgroundSetupHandle(mainActivity, setupCallbackHandle);
FlutterFirebaseMessagingService.startBackgroundIsolate(mainActivity, setupCallbackHandle);
FlutterFirebaseMessagingService.setBackgroundMessageHandle(
mainActivity, backgroundMessageHandle);
result.success(true);
} else if ("FcmDartService#initialized".equals(call.method)) {
FlutterFirebaseMessagingService.onInitialized();
FlutterFirebaseMessagingService.startBackgroundIsolate(
new BackgroundIsolateMethodCallHandler(), mainActivity, setupCallbackHandle);
result.success(true);
} else if ("configure".equals(call.method)) {
FirebaseInstanceId.getInstance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterCallbackInformation;
Expand Down Expand Up @@ -63,6 +64,13 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService {

private static Context backgroundContext;

/**
* flag, so we don't create multiple background views and communication channels as it seems that
* multiple {@link MethodChannel}s won't return an answer that is waited by @{link {@link
* LatchResult}} to release the thread.
*/
private static AtomicBoolean isBackgroundInitialized = new AtomicBoolean(false);

@Override
public void onCreate() {
super.onCreate();
Expand All @@ -74,7 +82,8 @@ public void onCreate() {
if (!isIsolateRunning.get()) {
SharedPreferences p = backgroundContext.getSharedPreferences(SHARED_PREFERENCES_KEY, 0);
long callbackHandle = p.getLong(BACKGROUND_SETUP_CALLBACK_HANDLE_KEY, 0);
startBackgroundIsolate(backgroundContext, callbackHandle);
startBackgroundIsolate(
new BackgroundIsolateMethodCallHandler(), backgroundContext, callbackHandle);
}
}

Expand Down Expand Up @@ -134,34 +143,55 @@ public void onNewToken(String token) {
* side. Called either by the plugin when the app is starting up or when the app receives a
* message while it is inactive.
*
* @param methodCallHandler a method call handler impl that handles the call from dart side.
* {@link BackgroundIsolateMethodCallHandler}.
* @param context Registrar or FirebaseMessagingService context.
* @param callbackHandle Handle used to retrieve the Dart function that sets up background
* handling on the dart side.
*/
public static void startBackgroundIsolate(Context context, long callbackHandle) {
public static void startBackgroundIsolate(
final BackgroundIsolateMethodCallHandler methodCallHandler,
Context context,
long callbackHandle) {
FlutterMain.ensureInitializationComplete(context, null);
String appBundlePath = FlutterMain.findAppBundlePath();
FlutterCallbackInformation flutterCallback =
FlutterCallbackInformation.lookupCallbackInformation(callbackHandle);
if (flutterCallback == null) {
Log.e(TAG, "Fatal: failed to find callback");
return;
}

// Note that we're passing `true` as the second argument to our
// FlutterNativeView constructor. This specifies the FlutterNativeView
// as a background view and does not create a drawing surface.
backgroundFlutterView = new FlutterNativeView(context, true);
if (appBundlePath != null && !isIsolateRunning.get()) {
if (pluginRegistrantCallback == null) {
throw new RuntimeException("PluginRegistrantCallback is not set.");
if (!isBackgroundInitialized.get()) {
isBackgroundInitialized.set(true);

String appBundlePath = FlutterMain.findAppBundlePath();
FlutterCallbackInformation flutterCallback =
FlutterCallbackInformation.lookupCallbackInformation(callbackHandle);
if (flutterCallback == null) {
Log.e(TAG, "Fatal: failed to find callback");

isBackgroundInitialized.set(false);
return;
}

// Note that we're passing `true` as the second argument to our
// FlutterNativeView constructor. This specifies the FlutterNativeView
// as a background view and does not create a drawing surface.
backgroundFlutterView = new FlutterNativeView(context, true);

backgroundChannel =
new MethodChannel(
backgroundFlutterView.getDartExecutor().getBinaryMessenger(),
"plugins.flutter.io/firebase_messaging_background");
backgroundChannel.setMethodCallHandler(methodCallHandler);

if (appBundlePath != null && !isIsolateRunning.get()) {
if (pluginRegistrantCallback == null) {
isBackgroundInitialized.set(false);

throw new RuntimeException("PluginRegistrantCallback is not set.");
}
FlutterRunArguments args = new FlutterRunArguments();
args.bundlePath = appBundlePath;
args.entrypoint = flutterCallback.callbackName;
args.libraryPath = flutterCallback.callbackLibraryPath;
backgroundFlutterView.runFromBundle(args);
pluginRegistrantCallback.registerWith(backgroundFlutterView.getPluginRegistry());
}
FlutterRunArguments args = new FlutterRunArguments();
args.bundlePath = appBundlePath;
args.entrypoint = flutterCallback.callbackName;
args.libraryPath = flutterCallback.callbackLibraryPath;
backgroundFlutterView.runFromBundle(args);
pluginRegistrantCallback.registerWith(backgroundFlutterView.getPluginRegistry());
}
}

Expand All @@ -188,9 +218,9 @@ public static void onInitialized() {
*
* @param channel Background method channel.
*/
public static void setBackgroundChannel(MethodChannel channel) {
backgroundChannel = channel;
}
// public static void setBackgroundChannel(MethodChannel channel) {
// backgroundChannel = channel;
// }

/**
* Set the background message handle for future use. When background messages need to be handled
Expand Down Expand Up @@ -330,3 +360,14 @@ private static boolean isApplicationForeground(Context context) {
return false;
}
}

class BackgroundIsolateMethodCallHandler implements MethodChannel.MethodCallHandler {

@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if ("FcmDartService#initialized".equals(call.method)) {
FlutterFirebaseMessagingService.onInitialized();
result.success(true);
}
}
}