Skip to content

Commit

Permalink
Support a model where the application creates a FlutterNativeView tha…
Browse files Browse the repository at this point in the history
…t is never destroyed (flutter#5256)

This allows applications to start a Flutter engine instance during app startup
and keep it running throughout the app process' lifetime.

FlutterActivity subclasses can override createFlutterNativeView to provide a
preinitialized FlutterNativeView instance and override retainFlutterNativeView
to signal that the FlutterNativeView should be kept alive when the activity
is destroyed.
  • Loading branch information
jason-simmons committed May 15, 2018
1 parent e463e80 commit c7ab033
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 4 deletions.
5 changes: 5 additions & 0 deletions shell/platform/android/io/flutter/app/FlutterActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public FlutterNativeView createFlutterNativeView() {
return null;
}

@Override
public boolean retainFlutterNativeView() {
return false;
}

@Override
public final boolean hasPlugin(String key) {
return pluginRegistry.hasPlugin(key);
Expand Down
19 changes: 15 additions & 4 deletions shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ public final class FlutterActivityDelegate
public interface ViewFactory {
FlutterView createFlutterView(Context context);
FlutterNativeView createFlutterNativeView();

/**
* Hook for subclasses to indicate that the {@code FlutterNativeView}
* returned by {@link #createFlutterNativeView()} should not be destroyed
* when this activity is destroyed.
*/
boolean retainFlutterNativeView();
}

private final Activity activity;
Expand Down Expand Up @@ -172,9 +179,11 @@ public void onCreate(Bundle savedInstanceState) {
if (loadIntent(activity.getIntent(), reuseIsolate)) {
return;
}
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath != null) {
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath != null) {
flutterView.runFromBundle(appBundlePath, null, "main", reuseIsolate);
}
}
}

Expand Down Expand Up @@ -250,7 +259,7 @@ public void onDestroy() {
if (flutterView != null) {
final boolean detach =
flutterView.getPluginRegistry().onViewDestroy(flutterView.getFlutterNativeView());
if (detach) {
if (detach || viewFactory.retainFlutterNativeView()) {
// Detach, but do not destroy the FlutterView if a plugin
// expressed interest in its FlutterNativeView.
flutterView.detach();
Expand Down Expand Up @@ -343,7 +352,9 @@ private boolean loadIntent(Intent intent, boolean reuseIsolate) {
if (route != null) {
flutterView.setInitialRoute(route);
}
flutterView.runFromBundle(appBundlePath, intent.getStringExtra("snapshot"), "main", reuseIsolate);
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
flutterView.runFromBundle(appBundlePath, intent.getStringExtra("snapshot"), "main", reuseIsolate);
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public FlutterNativeView createFlutterNativeView() {
return null;
}

@Override
public boolean retainFlutterNativeView() {
return false;
}

@Override
public final boolean hasPlugin(String key) {
return pluginRegistry.hasPlugin(key);
Expand Down
16 changes: 16 additions & 0 deletions shell/platform/android/io/flutter/view/FlutterNativeView.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class FlutterNativeView implements BinaryMessenger {
private long mNativePlatformView;
private FlutterView mFlutterView;
private final Context mContext;
private boolean applicationIsRunning;

public FlutterNativeView(Context context) {
mContext = context;
Expand All @@ -45,6 +46,7 @@ public void destroy() {
mFlutterView = null;
nativeDestroy(mNativePlatformView);
mNativePlatformView = 0;
applicationIsRunning = false;
}

public FlutterPluginRegistry getPluginRegistry() {
Expand All @@ -71,12 +73,26 @@ public void assertAttached() {

public void runFromBundle(String bundlePath, String snapshotOverride, String entrypoint, boolean reuseRuntimeController) {
assertAttached();
if (applicationIsRunning)
throw new AssertionError("This Flutter engine instance is already running an application");

nativeRunBundleAndSnapshot(mNativePlatformView, bundlePath, snapshotOverride, entrypoint, reuseRuntimeController, mContext.getResources().getAssets());

applicationIsRunning = true;
}

public void runFromSource(final String assetsDirectory, final String main, final String packages) {
assertAttached();
if (applicationIsRunning)
throw new AssertionError("This Flutter engine instance is already running an application");

nativeRunBundleAndSource(mNativePlatformView, assetsDirectory, main, packages);

applicationIsRunning = true;
}

public boolean isApplicationRunning() {
return applicationIsRunning;
}

public void setAssetBundlePathOnUI(final String assetsDirectory) {
Expand Down

0 comments on commit c7ab033

Please sign in to comment.