Skip to content

Commit

Permalink
Android embedding API updates for plugin ecosystem - plugin facade, s…
Browse files Browse the repository at this point in the history
…plit Lifecycle, save state callbacks to plugins (#43241, #43242, #43295) (#13280)
  • Loading branch information
matthew-carroll authored Oct 25, 2019
1 parent e657661 commit 74d18bc
Show file tree
Hide file tree
Showing 14 changed files with 544 additions and 46 deletions.
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ action("robolectric_tests") {
"test/io/flutter/SmokeTest.java",
"test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java",
"test/io/flutter/embedding/android/FlutterActivityTest.java",
"test/io/flutter/embedding/android/FlutterAndroidComponentTest.java",
"test/io/flutter/embedding/android/FlutterFragmentTest.java",
"test/io/flutter/embedding/android/FlutterViewTest.java",
"test/io/flutter/embedding/engine/FlutterEngineCacheTest.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {

delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
delegate.onActivityCreated(savedInstanceState);

configureWindowForTransparency();
setContentView(createFlutterView());
Expand Down Expand Up @@ -557,6 +558,12 @@ protected void onStop() {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
delegate.onSaveInstanceState(outState);
}

@Override
protected void onDestroy() {
super.onDestroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,6 @@ FlutterEngine getFlutterEngine() {
void onAttach(@NonNull Context context) {
ensureAlive();

initializeFlutter(context);

This comment has been minimized.

Copy link
@xster

xster Oct 25, 2019

Member

ha, I was gonna file a bug to mention that we missed this the first time around. Thanks for cleaning this up.


// When "retain instance" is true, the FlutterEngine will survive configuration
// changes. Therefore, we create a new one only if one does not already exist.
if (flutterEngine == null) {
Expand Down Expand Up @@ -178,13 +176,6 @@ void onAttach(@NonNull Context context) {
host.configureFlutterEngine(flutterEngine);
}

private void initializeFlutter(@NonNull Context context) {
FlutterMain.ensureInitializationComplete(
context.getApplicationContext(),
host.getFlutterShellArgs().toArray()
);
}

/**
* Obtains a reference to a FlutterEngine to back this delegate and its {@code host}.
* <p>
Expand Down Expand Up @@ -223,7 +214,7 @@ private void setupFlutterEngine() {
// FlutterView.
Log.d(TAG, "No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
+ " this FlutterFragment.");
flutterEngine = new FlutterEngine(host.getContext());
flutterEngine = new FlutterEngine(host.getContext(), host.getFlutterShellArgs().toArray());
isFlutterEngineFromHost = false;
}

Expand Down Expand Up @@ -257,6 +248,15 @@ View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nulla
return flutterSplashView;
}

void onActivityCreated(@Nullable Bundle bundle) {
Log.v(TAG, "onActivityCreated. Giving plugins an opportunity to restore state.");
ensureAlive();

if (host.shouldAttachEngineToActivity()) {
flutterEngine.getActivityControlSurface().onRestoreInstanceState(bundle);
}
}

/**
* Invoke this from {@code Activity#onStart()} or {@code Fragment#onStart()}.
* <p>
Expand Down Expand Up @@ -404,6 +404,15 @@ void onDestroyView() {
flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
}

void onSaveInstanceState(@Nullable Bundle bundle) {
Log.v(TAG, "onSaveInstanceState. Giving plugins an opportunity to save state.");
ensureAlive();

if (host.shouldAttachEngineToActivity()) {
flutterEngine.getActivityControlSurface().onSaveInstanceState(bundle);
}
}

/**
* Invoke this from {@code Activity#onDestroy()} or {@code Fragment#onDetach()}.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,12 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
return delegate.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
delegate.onActivityCreated(savedInstanceState);
}

@Override
public void onStart() {
super.onStart();
Expand Down Expand Up @@ -622,6 +628,12 @@ public void onDestroyView() {
delegate.onDestroyView();
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
delegate.onSaveInstanceState(outState);
}

@Override
public void onDetach() {
super.onDetach();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -143,18 +144,45 @@ public void onPreEngineRestart() {
* and {@link FlutterLoader#ensureInitializationComplete(Context, String[])}.
*/
public FlutterEngine(@NonNull Context context) {
this(context, FlutterLoader.getInstance(), new FlutterJNI());
this(context, null);
}

/**
* Same as {@link #FlutterEngine(Context)} with added support for passing Dart
* VM arguments.
* <p>
* If the Dart VM has already started, the given arguments will have no effect.
*/
public FlutterEngine(@NonNull Context context, @Nullable String[] dartVmArgs) {
this(context, FlutterLoader.getInstance(), new FlutterJNI(), dartVmArgs);
}

/**
* Same as {@link #FlutterEngine(Context, FlutterLoader, FlutterJNI, String[])} but with no Dart
* VM flags.
*/
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI
) {
this(context, flutterLoader, flutterJNI, null);
}

/**
* Constructs a new {@code FlutterEngine}. See {@link #FlutterEngine(Context)}.
*
* {@code flutterJNI} should be a new instance that has never been attached to an engine before.
*/
public FlutterEngine(@NonNull Context context, @NonNull FlutterLoader flutterLoader, @NonNull FlutterJNI flutterJNI) {
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@Nullable String[] dartVmArgs
) {
this.flutterJNI = flutterJNI;
flutterLoader.startInitialization(context);
flutterLoader.ensureInitializationComplete(context, null);
flutterLoader.ensureInitializationComplete(context, dartVmArgs);

flutterJNI.addEngineLifecycleListener(engineLifecycleListener);
attachToJni();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

Expand Down Expand Up @@ -49,6 +50,8 @@ class FlutterEnginePluginRegistry implements PluginRegistry,

// Standard FlutterPlugin
@NonNull
private final FlutterEngine flutterEngine;
@NonNull
private final FlutterPlugin.FlutterPluginBinding pluginBinding;
@NonNull
private final FlutterEngineAndroidLifecycle flutterEngineAndroidLifecycle;
Expand Down Expand Up @@ -91,10 +94,14 @@ class FlutterEnginePluginRegistry implements PluginRegistry,
@NonNull FlutterEngine flutterEngine,
@NonNull FlutterEngineAndroidLifecycle lifecycle
) {
this.flutterEngine = flutterEngine;
flutterEngineAndroidLifecycle = lifecycle;
pluginBinding = new FlutterPlugin.FlutterPluginBinding(
appContext,
flutterEngine,
flutterEngine.getDartExecutor(),
flutterEngine.getRenderer(),
flutterEngine.getPlatformViewsController().getRegistry(),
lifecycle
);
}
Expand Down Expand Up @@ -288,16 +295,16 @@ public void attachToActivity(
detachFromAndroidComponent();

this.activity = activity;
this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity);
this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity, lifecycle);
this.flutterEngineAndroidLifecycle.setBackingLifecycle(lifecycle);

// Activate the PlatformViewsController. This must happen before any plugins attempt
// to use it, otherwise an error stack trace will appear that says there is no
// flutter/platform_views channel.
pluginBinding.getFlutterEngine().getPlatformViewsController().attach(
flutterEngine.getPlatformViewsController().attach(
activity,
pluginBinding.getFlutterEngine().getRenderer(),
pluginBinding.getFlutterEngine().getDartExecutor()
flutterEngine.getRenderer(),
flutterEngine.getDartExecutor()
);

// Notify all ActivityAware plugins that they are now attached to a new Activity.
Expand All @@ -322,7 +329,7 @@ public void detachFromActivityForConfigChanges() {
}

// Deactivate PlatformViewsController.
pluginBinding.getFlutterEngine().getPlatformViewsController().detach();
flutterEngine.getPlatformViewsController().detach();

flutterEngineAndroidLifecycle.setBackingLifecycle(null);
activity = null;
Expand All @@ -341,7 +348,7 @@ public void detachFromActivity() {
}

// Deactivate PlatformViewsController.
pluginBinding.getFlutterEngine().getPlatformViewsController().detach();
flutterEngine.getPlatformViewsController().detach();

flutterEngineAndroidLifecycle.setBackingLifecycle(null);
activity = null;
Expand Down Expand Up @@ -392,6 +399,26 @@ public void onUserLeaveHint() {
Log.e(TAG, "Attempted to notify ActivityAware plugins of onUserLeaveHint, but no Activity was attached.");
}
}

@Override
public void onSaveInstanceState(@NonNull Bundle bundle) {
Log.v(TAG, "Forwarding onSaveInstanceState() to plugins.");
if (isAttachedToActivity()) {
activityPluginBinding.onSaveInstanceState(bundle);
} else {
Log.e(TAG, "Attempted to notify ActivityAware plugins of onSaveInstanceState, but no Activity was attached.");
}
}

@Override
public void onRestoreInstanceState(@Nullable Bundle bundle) {
Log.v(TAG, "Forwarding onRestoreInstanceState() to plugins.");
if (isAttachedToActivity()) {
activityPluginBinding.onRestoreInstanceState(bundle);
} else {
Log.e(TAG, "Attempted to notify ActivityAware plugins of onRestoreInstanceState, but no Activity was attached.");
}
}
//------- End ActivityControlSurface -----

//----- Start ServiceControlSurface ----
Expand All @@ -400,13 +427,13 @@ private boolean isAttachedToService() {
}

@Override
public void attachToService(@NonNull Service service, @NonNull Lifecycle lifecycle, boolean isForeground) {
public void attachToService(@NonNull Service service, @Nullable Lifecycle lifecycle, boolean isForeground) {
Log.v(TAG, "Attaching to a Service: " + service);
// If we were already attached to an Android component, detach from it.
detachFromAndroidComponent();

this.service = service;
this.servicePluginBinding = new FlutterEngineServicePluginBinding(service);
this.servicePluginBinding = new FlutterEngineServicePluginBinding(service, lifecycle);
flutterEngineAndroidLifecycle.setBackingLifecycle(lifecycle);

// Notify all ServiceAware plugins that they are now attached to a new Service.
Expand Down Expand Up @@ -523,16 +550,21 @@ private static class FlutterEngineActivityPluginBinding implements ActivityPlugi
@NonNull
private final Activity activity;
@NonNull
private final Lifecycle lifecycle;
@NonNull
private final Set<io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener> onRequestPermissionsResultListeners = new HashSet<>();
@NonNull
private final Set<io.flutter.plugin.common.PluginRegistry.ActivityResultListener> onActivityResultListeners = new HashSet<>();
@NonNull
private final Set<io.flutter.plugin.common.PluginRegistry.NewIntentListener> onNewIntentListeners = new HashSet<>();
@NonNull
private final Set<io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener> onUserLeaveHintListeners = new HashSet<>();
@NonNull
private final Set<OnSaveInstanceStateListener> onSaveInstanceStateListeners = new HashSet<>();

public FlutterEngineActivityPluginBinding(@NonNull Activity activity) {
public FlutterEngineActivityPluginBinding(@NonNull Activity activity, @NonNull Lifecycle lifecycle) {
this.activity = activity;
this.lifecycle = lifecycle;
}

@Override
Expand All @@ -541,6 +573,12 @@ public Activity getActivity() {
return activity;
}

@NonNull
@Override
public Lifecycle getLifecycle() {
return lifecycle;
}

@Override
public void addRequestPermissionsResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener) {
onRequestPermissionsResultListeners.add(listener);
Expand Down Expand Up @@ -615,6 +653,16 @@ public void removeOnUserLeaveHintListener(@NonNull io.flutter.plugin.common.Plug
onUserLeaveHintListeners.remove(listener);
}

@Override
public void addOnSaveStateListener(@NonNull OnSaveInstanceStateListener listener) {
onSaveInstanceStateListeners.add(listener);
}

@Override
public void removeOnSaveStateListener(@NonNull OnSaveInstanceStateListener listener) {
onSaveInstanceStateListeners.remove(listener);
}

/**
* Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its
* associated {@link Activity} has its {@code onUserLeaveHint()} method invoked.
Expand All @@ -624,16 +672,41 @@ void onUserLeaveHint() {
listener.onUserLeaveHint();
}
}

/**
* Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its
* associated {@link Activity} or {@code Fragment} has its {@code onSaveInstanceState(Bundle)}
* method invoked.
*/
void onSaveInstanceState(@NonNull Bundle bundle) {
for (OnSaveInstanceStateListener listener : onSaveInstanceStateListeners) {
listener.onSaveInstanceState(bundle);
}
}

/**
* Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its
* associated {@link Activity} has its {@code onCreate(Bundle)} method invoked, or its
* associated {@code Fragment} has its {@code onActivityCreated(Bundle)} method invoked.
*/
void onRestoreInstanceState(@Nullable Bundle bundle) {
for (OnSaveInstanceStateListener listener : onSaveInstanceStateListeners) {
listener.onRestoreInstanceState(bundle);
}
}
}

private static class FlutterEngineServicePluginBinding implements ServicePluginBinding {
@NonNull
private final Service service;
@Nullable
private final Lifecycle lifecycle;
@NonNull
private final Set<ServiceAware.OnModeChangeListener> onModeChangeListeners = new HashSet<>();

FlutterEngineServicePluginBinding(@NonNull Service service) {
FlutterEngineServicePluginBinding(@NonNull Service service, @Nullable Lifecycle lifecycle) {
this.service = service;
this.lifecycle = lifecycle;
}

@Override
Expand All @@ -642,6 +715,12 @@ public Service getService() {
return service;
}

@Nullable
@Override
public Lifecycle getLifecycle() {
return lifecycle;
}

@Override
public void addOnModeChangeListener(@NonNull ServiceAware.OnModeChangeListener listener) {
onModeChangeListeners.add(listener);
Expand Down
Loading

0 comments on commit 74d18bc

Please sign in to comment.