diff --git a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index ff0f621b6ae5a..b0f4b165416de 100644 --- a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -234,6 +234,7 @@ public void ensureInitializationCompleteAsync( "ensureInitializationComplete must be called after startInitialization"); } if (initialized) { + callbackHandler.post(callback); return; } new Thread( diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewActivity.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewActivity.java index 4772203345d95..366ffe75178b0 100644 --- a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewActivity.java +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TextPlatformViewActivity.java @@ -1,22 +1,26 @@ package dev.flutter.scenarios; import android.Manifest; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.support.annotation.NonNull; import io.flutter.Log; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; +import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryCodec; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicBoolean; public class TextPlatformViewActivity extends FlutterActivity { static final String TAG = "Scenarios"; @@ -31,7 +35,19 @@ protected void onCreate(Bundle savedInstanceState) { } // Run for one minute, get the timeline data, write it, and finish. final Uri logFileUri = launchIntent.getData(); - new Handler().postDelayed(() -> writeTimelineData(logFileUri), 20000); + new Handler() + .postDelayed( + new Runnable() { + @Override + public void run() { + writeTimelineData(logFileUri); + + testFlutterLoaderCallbackWhenInitializedTwice(); + } + }, + 20000); + } else { + testFlutterLoaderCallbackWhenInitializedTwice(); } } @@ -80,4 +96,46 @@ private void writeTimelineData(Uri logFile) { finish(); }); } + + /** + * This method verifies that {@link FlutterLoader#ensureInitializationCompleteAsync(Context, + * String[], Handler, Runnable)} invokes its callback when called after initialization. + */ + private void testFlutterLoaderCallbackWhenInitializedTwice() { + FlutterLoader flutterLoader = new FlutterLoader(); + + // Flutter is probably already loaded in this app based on + // code that ran before this method. Nonetheless, invoke the + // blocking initialization here to ensure it's initialized. + flutterLoader.startInitialization(getApplicationContext()); + flutterLoader.ensureInitializationComplete(getApplication(), new String[] {}); + + // Now that Flutter is loaded, invoke ensureInitializationCompleteAsync with + // a callback and verify that the callback is invoked. + Handler mainHandler = new Handler(Looper.getMainLooper()); + + final AtomicBoolean didInvokeCallback = new AtomicBoolean(false); + + flutterLoader.ensureInitializationCompleteAsync( + getApplication(), + new String[] {}, + mainHandler, + new Runnable() { + @Override + public void run() { + didInvokeCallback.set(true); + } + }); + + mainHandler.post( + new Runnable() { + @Override + public void run() { + if (!didInvokeCallback.get()) { + throw new RuntimeException( + "Failed test: FlutterLoader#ensureInitializationCompleteAsync() did not invoke its callback."); + } + } + }); + } }