public void setup(Application application, FlutterBoostDelegate delegate, Callback callback, FlutterBoostOptions options) {
// 1. initialize default engine
FlutterEngine engine = FlutterEngineCache.getInstance().get(ENGINE_ID);
if (engine == null) {
if (options == null) options = FlutterBoostOptions.createDefault();
engine = new FlutterEngine(application, options.shellArgs());
engine.getNavigationChannel().setInitialRoute(options.initialRoute());
engine.getDartExecutor().executeDartEntrypoint(new DartExecutor.DartEntrypoint(
FlutterMain.findAppBundlePath(), options.dartEntrypoint()));
if(callback != null) callback.onStart(engine);
FlutterEngineCache.getInstance().put(ENGINE_ID, engine);
}
// 2. set delegate
getPlugin().setDelegate(delegate);
//3. register ActivityLifecycleCallbacks
setupActivityLifecycleCallback(application);
}
public FlutterEngine(@NonNull Context context, @Nullable String[] dartVmArgs) {
this(context, /* flutterLoader */ null, new FlutterJNI(), dartVmArgs, true);//最后的参数true表示automaticallyRegisterPlugins
}
public FlutterEngine(
@NonNull Context context,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@Nullable String[] dartVmArgs,
boolean automaticallyRegisterPlugins) {
this(
context,
flutterLoader,
flutterJNI,
new PlatformViewsController(),
dartVmArgs,
automaticallyRegisterPlugins);
}
//最终在FlutterEngine构造方法结尾进行如下操作
if (automaticallyRegisterPlugins) {
registerPlugins();
}
//反射调用自动生成的注册类的注册方法
private void registerPlugins() {
try {
Class<?> generatedPluginRegistrant =
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method registrationMethod =
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, this);
} catch (Exception e) {
Log.w(
TAG,
"Tried to automatically register plugins with FlutterEngine ("
+ this
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
}
}
package io.flutter.plugins;
/**
* Generated file. Do not edit.
* This file is generated by the Flutter tool based on the
* plugins that support the Android platform.
*/
@Keep
public final class GeneratedPluginRegistrant {
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new com.idlefish.flutterboost.FlutterBoostPlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.imagepicker.ImagePickerPlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.videoplayer.VideoPlayerPlugin());
}
}
public FlutterBoostPlugin getPlugin() {
if (plugin == null) {
FlutterEngine engine = FlutterEngineCache.getInstance().get(ENGINE_ID);
if (engine == null) {
throw new RuntimeException("FlutterBoost might *not* have been initialized yet!!!");
}
plugin = getFlutterBoostPlugin(engine);
}
return plugin;
}
public class FlutterBoostPlugin implements FlutterPlugin, Messages.NativeRouterApi {
@Override
public void onAttachedToEngine(FlutterPluginBinding binding) {
Messages.NativeRouterApi.setup(binding.getBinaryMessenger(), this);
channel = new Messages.FlutterRouterApi(binding.getBinaryMessenger());
}
@Override
public void onDetachedFromEngine(FlutterPluginBinding binding) {
channel = null;
}
}
private void setupActivityLifecycleCallback(Application application) {
application.registerActivityLifecycleCallbacks(new BoostActivityLifecycle());
}
private class BoostActivityLifecycle implements Application.ActivityLifecycleCallbacks {
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
private void dispatchForegroundEvent() {
FlutterBoost.instance().setAppIsInBackground(false);
FlutterBoost.instance().getPlugin().onForeground();
}
private void dispatchBackgroundEvent() {
FlutterBoost.instance().setAppIsInBackground(true);
FlutterBoost.instance().getPlugin().onBackground();
}
}
对应FlutterBoostDelegate中pushFlutterRoute方法的执行
Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity.class, FlutterBoost.ENGINE_ID)
.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.opaque)
.destroyEngineWithActivity(false)
.url("flutterPage")
.urlParams(params)
.build(this);
startActivityForResult(intent, REQUEST_CODE);
public class FlutterBoostActivity extends FlutterActivity implements FlutterViewContainer {
private static final String TAG = "FlutterBoostActivity";
private FlutterView flutterView;
private FlutterViewContainerObserver observer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
observer = FlutterBoostPlugin.ContainerShadowNode.create(this, FlutterBoost.instance().getPlugin());
observer.onCreateView();
}
@Override
public void onResume() {
if (flutterView == null) {
findFlutterView(getWindow().getDecorView());//递归遍历找到flutterView
}
super.onResume();
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
if (FlutterBoost.instance().isAppInBackground() &&
!FlutterBoost.instance().getPlugin().isTopContainer(getUniqueId())) {
Log.w(TAG, "Unexpected activity lifecycle event on Android Q. " +
"See https://issuetracker.google.com/issues/185693011 for more details.");
return;
}
}
observer.onAppear();//main
ActivityAndFragmentPatch.onResumeAttachToFlutterEngine(flutterView,
getFlutterEngine(), this);//main
}
//class ContainerShadowNode
@Override
public void onAppear() {
plugin.reorderContainer(getUniqueId(), this);
plugin.pushRoute(getUniqueId(), getUrl(), getUrlParams(), null);//main
plugin.onContainerShow(getUniqueId());
Log.v(TAG, "#onAppear: " + getUniqueId() + ", " + plugin.getContainers());
}
//FlutterBoostPlugin
public void pushRoute(String uniqueId, String pageName, Map<String, Object> arguments,
final Reply<Void> callback) {
if (channel != null) {
Messages.CommonParams params = new Messages.CommonParams();
params.setUniqueId(uniqueId);
params.setPageName(pageName);
params.setArguments((Map<Object, Object>)(Object) arguments);
channel.pushRoute(params, reply -> {
if (callback != null) {
callback.reply(null);
}
});
} else {
throw new RuntimeException("FlutterBoostPlugin might *NOT* have attached to engine yet!");
}
}
/// The MessageChannel counterpart on the Dart side.
class BoostFlutterRouterApi extends FlutterRouterApi {
final FlutterBoostAppState appState;
static BoostFlutterRouterApi _instance;
@override
void pushRoute(CommonParams arg) {
appState.push(
arg.pageName,
uniqueId: arg.uniqueId,
arguments:
Map<String, dynamic>.from(arg.arguments ?? <String, dynamic>{}),
withContainer: true,
);
}
void push(String pageName,
{String uniqueId, Map<String, dynamic> arguments, bool withContainer}) {
_cancelActivePointers();
final existed = _findContainerByUniqueId(uniqueId);
if (existed != null) {
if (topContainer?.pageInfo?.uniqueId != uniqueId) {
containers.remove(existed);
containers.add(existed);
//move the overlayEntry which matches this existing container to the top
refreshOnMoveToTop(existed);
}
} else {
final pageInfo = PageInfo(
pageName: pageName,
uniqueId: uniqueId ?? _createUniqueId(pageName),
arguments: arguments,
withContainer: withContainer);
if (withContainer) {
final container = _createContainer(pageInfo);
final previousContainer = topContainer;
containers.add(container);
BoostLifecycleBinding.instance
.containerDidPush(container, previousContainer);
// Add a new overlay entry with this container
refreshOnPush(container);//main
} else {
// In this case , we don't need to change the overlayEntries data,
// so we don't call any refresh method
topContainer.pages.add(BoostPage.create(pageInfo));
topContainer.refresh();
}
}
Logger.log('push page, uniqueId=$uniqueId, existed=$existed,'
' withContainer=$withContainer, arguments:$arguments, $containers');
}
void refreshOnPush(BoostContainer container) {
refreshSpecificOverlayEntries(container, BoostSpecificEntryRefreshMode.add);
assert(() {
_saveStackForHotRestart();
return true;
}());
}
///Refresh an specific entry instead of all of entries to enhance the performace
///
///[container] : The container you want to operate, it is related with
/// internal [OverlayEntry]
///[mode] : The [BoostSpecificEntryRefreshMode] you want to choose
void refreshSpecificOverlayEntries(
BoostContainer container, BoostSpecificEntryRefreshMode mode) {
//Get OverlayState from global key
final overlayState = overlayKey.currentState;
if (overlayState == null) {
return;
}
final hasScheduledFrame = SchedulerBinding.instance.hasScheduledFrame;
final framesEnabled = SchedulerBinding.instance.framesEnabled;
//deal with different situation
switch (mode) {
case BoostSpecificEntryRefreshMode.add:
final entry = _ContainerOverlayEntry(container);
_lastEntries.add(entry);
overlayState.insert(entry);//main
break;
/// Insert the given entry into the overlay.
///
/// If `below` is non-null, the entry is inserted just below `below`.
/// If `above` is non-null, the entry is inserted just above `above`.
/// Otherwise, the entry is inserted on top.
///
/// It is an error to specify both `above` and `below`.
void insert(OverlayEntry entry, { OverlayEntry? below, OverlayEntry? above }) {
assert(_debugVerifyInsertPosition(above, below));
assert(!_entries.contains(entry), 'The specified entry is already present in the Overlay.');
assert(entry._overlay == null, 'The specified entry is already present in another Overlay.');
entry._overlay = this;
setState(() {
_entries.insert(_insertionIndex(below, above), entry);
});
}
public static void onResumeAttachToFlutterEngine(FlutterView flutterView, FlutterEngine flutterEngine, FlutterViewContainer container) {
flutterView.attachToFlutterEngine(flutterEngine);//main
flutterEngine.getLifecycleChannel().appIsResumed();
}
直接启动即可
context.startActivity(intent);
BoostNavigator.instance.push("native"),
/// A object that manages a set of pages with a hybrid stack.
///
class BoostNavigator {
BoostNavigator._();
static final BoostNavigator _instance = BoostNavigator._();
static BoostNavigator get instance {
_instance.appState ??= overlayKey.currentContext
?.findAncestorStateOfType<FlutterBoostAppState>();
return _instance;
}
}
/// Push the page with the given [name] onto the hybrid stack.
Future<T> push<T extends Object>(String name,
{Map<String, dynamic> arguments, bool withContainer = false}) async {
return future.then((dynamic _state) {
final state = _state as InterceptorState<dynamic>;
if (state.data is BoostInterceptorOption) {
assert(state.type == InterceptorResultType.next);
pushOption = state.data;
if (isFlutterPage(pushOption.name)) {//根据main.dart中的routeFactory和配置的routerMap,判断是否是flutter page
//flutter start flutter page
return appState.pushWithResult(pushOption.name,
arguments: pushOption.arguments, withContainer: withContainer);
} else {
//flutter start native page
final params = CommonParams()
..pageName = pushOption.name
..arguments = pushOption.arguments;
appState.nativeRouterApi.pushNativeRoute(params);
return appState.pendNativeResult(pushOption.name);
}
} else {
assert(state.type == InterceptorResultType.resolve);
return Future<T>.value(state.data as T);
}
});
// Autogenerated from Pigeon
class NativeRouterApi {
Future<void> pushNativeRoute(CommonParams arg) async {
final Object encoded = arg.encode();
const BasicMessageChannel<Object> channel =
BasicMessageChannel<Object>('dev.flutter.pigeon.NativeRouterApi.pushNativeRoute', StandardMessageCodec());
final Map<Object, Object> replyMap = await channel.send(encoded) as Map<Object, Object>;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
details: null,
);
} else if (replyMap['error'] != null) {
final Map<Object, Object> error = (replyMap['error'] as Map<Object, Object>);
throw PlatformException(
code: (error['code'] as String),
message: error['message'] as String,
details: error['details'],
);
} else {
// noop
}
}
// Autogenerated from Pigeon (v0.1.23)
/** Sets up an instance of `NativeRouterApi` to handle messages through the `binaryMessenger`. */
static void setup(BinaryMessenger binaryMessenger, NativeRouterApi api) {
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.NativeRouterApi.pushNativeRoute", new StandardMessageCodec());
if (api != null) {
channel.setMessageHandler((message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
@SuppressWarnings("ConstantConditions")
CommonParams input = CommonParams.fromMap((Map<String, Object>)message);
api.pushNativeRoute(input);//main
wrapped.put("result", null);
}
catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
//FlutterBoostPlugin
@Override
public void pushNativeRoute(Messages.CommonParams params) {
if (delegate != null) {
delegate.pushNativeRoute(params.getPageName(), (Map<String, Object>) (Object)params.getArguments());
} else {
throw new RuntimeException("FlutterBoostPlugin might *NOT* set delegate!");
}
}
public class MyFlutterBoostDelegate implements FlutterBoostDelegate {
private final int REQUEST_CODE = 999;
@Override
public void pushNativeRoute(String pageName, Map<String, Object> arguments) {
Intent intent = new Intent(FlutterBoost.instance().currentActivity(), NativePageActivity.class);
FlutterBoost.instance().currentActivity().startActivityForResult(intent, REQUEST_CODE);
}
继续上述的
return future.then((dynamic _state) {
final state = _state as InterceptorState<dynamic>;
if (state.data is BoostInterceptorOption) {
assert(state.type == InterceptorResultType.next);
pushOption = state.data;
if (isFlutterPage(pushOption.name)) {//flutter start flutter page
return appState.pushWithResult(pushOption.name,
arguments: pushOption.arguments, withContainer: withContainer);
Future<T> pushWithResult<T extends Object>(String pageName,
{String uniqueId, Map<String, dynamic> arguments, bool withContainer}) {
final completer = Completer<T>();
assert(uniqueId == null);
uniqueId = _createUniqueId(pageName);
if (withContainer) {//如果需要native层Activity容器包裹即将启动的页面
final params = CommonParams()
..pageName = pageName
..uniqueId = uniqueId
..arguments = arguments ?? <String, dynamic>{};
nativeRouterApi.pushFlutterRoute(params);//channel通知native启动flutter页面
} else {//否则,push到overlay
push(pageName,
uniqueId: uniqueId, arguments: arguments, withContainer: false);
}
_pendingResult[uniqueId] = completer;
return completer.future;
}