FlutterBoost

BoostFlutterActivity.onCreate

BoostFlutterActivity implements FlutterActivityAndFragmentDelegate.Host {
  private FlutterActivityAndFragmentDelegate delegate;
  
  public static class NewEngineIntentBuilder {

  public Intent build(@NonNull Context context) {

   SerializableMap serializableMap = new SerializableMap();
   serializableMap.setMap(params);

   return new Intent(context, activityClass)
       .putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
       .putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false)
       .putExtra(EXTRA_URL, url)
       .putExtra(EXTRA_PARAMS, serializableMap);
 }

@Override
 public String getContainerUrl() {
   if (getIntent().hasExtra(EXTRA_URL)) {
     return getIntent().getStringExtra(EXTRA_URL);
   }
}

@Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
   delegate = new FlutterActivityAndFragmentDelegate(this);
   delegate.onAttach(this);

   setContentView(createFlutterView());
 }
FlutterActivityAndFragmentDelegate implements IFlutterViewContainer {
  protected IOperateSyncer mSyncer;

 FlutterActivityAndFragmentDelegate(@NonNull Host host) {
   this.host = host;
 }

 void onAttach(@NonNull Context context) {
   // 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) {
     setupFlutterEngine();
   }
  }
  
  private void setupFlutterEngine() {
    // Second, defer to subclasses for a custom FlutterEngine.
    flutterEngine = host.provideFlutterEngine(host.getContext());
    if (flutterEngine != null) {
      isFlutterEngineFromHost = true;
      return;
    }
  }
}

provideFlutterEngine

/**
 \* Hook for subclasses to easily provide a custom {@link FlutterEngine}.
 \* <p>
 \* This hook is where a cached {@link FlutterEngine} should be provided, if a cached
 \* {@link FlutterEngine} is desired.
 */
 @Nullable
 @Override
 public FlutterEngine provideFlutterEngine(@NonNull Context context) {
   // No-op. Hook for subclasses.
   return FlutterBoost.instance().engineProvider();
 }

setContentView(createFlutterView())

@NonNull
 protected View createFlutterView() {
   return delegate.onCreateView(
       null /* inflater */,
       null /* container */,
       null /* savedInstanceState */);
 }

View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
   Log.v(TAG, "Creating FlutterView.");
   flutterEngine.getActivityControlSurface().attachToActivity(
       host.getActivity(),
       host.getLifecycle()
   );

 mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
 mSyncer.onCreate();
}

mSyncer.onCreate()

ContainerRecord implements IContainerRecord implements IOperateSyncer {
  private MethodChannelProxy mProxy = new MethodChannelProxy();

  @Override
   public void onCreate() {
     mState = STATE_CREATED;
     mProxy.create();
   }
  
  private class MethodChannelProxy {
  private void create() {
     if (mState == STATE_UNKNOW) {
       invokeChannelUnsafe("didInitPageContainer",
           mContainer.getContainerUrl(),
           mContainer.getContainerUrlParams(),
           mUniqueId
       );
       //Debuger.log("didInitPageContainer");
       mState = STATE_CREATED;
     }
   }

  public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
     HashMap<String, Object> args = new HashMap<>();
     args.put("pageName", url);
     args.put("params", params);
     args.put("uniqueId", uniqueId);
     FlutterBoost.instance().channel().invokeMethodUnsafe(method, args);//goto dart ContainerCoordinator._onMethodCall,利用flutter自带的methodChannel机制,反向调用native->dart
   }
}

ContainerCoordinator._onMethodCall

//ContainerCoordinator
Future<dynamic> _onMethodCall(MethodCall call) {
  Logger.log("onMetohdCall ${call.method}");
 
  switch (call.method) {
   case "didInitPageContainer":
    {
     String pageName = call.arguments["pageName"];
     Map params = call.arguments["params"];
     String uniqueId = call.arguments["uniqueId"];
     _nativeContainerDidInit(pageName, params, uniqueId);
    }
    break;

case "didShowPageContainer":
  {
   String pageName = call.arguments["pageName"];
   Map params = call.arguments["params"];
   String uniqueId = call.arguments["uniqueId"];
   nativeContainerDidShow(pageName, params, uniqueId);
  }
  break;

_nativeContainerDidInit

bool _nativeContainerDidInit(String name, Map params, String pageId) {
  performContainerLifeCycle(_createContainerSettings(name, params, pageId),ContainerLifeCycle.Init);
  return true;
 }

_createContainerSettings

BoostContainerSettings _createContainerSettings(
   String name, Map params, String pageId) {
  Widget page;

  final BoostContainerSettings routeSettings = BoostContainerSettings(//main
    uniqueId: pageId,
    name: name,
    params: params,
    builder: (BuildContext ctx) {//main
     //Try to build a page using keyed builder.
     if (_pageBuilders[name] != null) {
      page = _pageBuilders[name](name, params, pageId);//main
     }

     //Build a page using default builder.
     if (page == null && _defaultPageBuilder != null) {
      page = _defaultPageBuilder(name, params, pageId);
     }
     
     assert(page != null);
     Logger.log('build widget:$page for page:$name($pageId)');
     
     return page;
    });

  return routeSettings;
 }

BoostFlutterActivity.onResume

@Override
 protected void onResume() {
   super.onResume();
   lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
   delegate.onResume();
 }
void onResume() {
   mSyncer.onAppear();//main

   ensureAlive();
   flutterEngine.getLifecycleChannel().appIsResumed();

   BoostPluginRegistry registry = (BoostPluginRegistry) FlutterBoost.instance().getPluginRegistry();
   ActivityPluginBinding binding = registry.getRegistrarAggregate().getActivityPluginBinding();
   if (binding != null && (binding.getActivity() != this.host.getActivity())) {
     flutterEngine.getActivityControlSurface().attachToActivity(
         host.getActivity(),
         host.getLifecycle()
     );
   }
 }

mSyncer.onAppear()

//ContainerRecord
@Override
 public void onAppear() {
   Utils.assertCallOnMainThread();
   if (mState != STATE_CREATED && mState != STATE_DISAPPEAR) {
     Debuger.exception("state error");
   }

   mState = STATE_APPEAR;
   mManager.pushRecord(this);
   mProxy.appear();
   mContainer.getBoostFlutterView().onAttach();
 }

ContainerCoordinator.nativeContainerDidShow

bool nativeContainerDidShow(String name, Map params, String pageId) {
  FlutterBoost.containerManager?.showContainer(_createContainerSettings(name, params, pageId));

ContainerManagerState.showContainer

BoostContainerManager extends StatefulWidget {
  @override
  ContainerManagerState createState() => ContainerManagerState();
}
ContainerManagerState extends State<BoostContainerManager> {
 BoostContainer _onstage;
  
 //Current visible container.
 BoostContainerState get onstageContainer => _stateOf(_onstage);
  
 List<_ContainerOverlayEntry> _leastEntries;
  
 final GlobalKey<OverlayState> _overlayKey = GlobalKey<OverlayState>();
  
  
 //If container exists bring it to front else create a container.
 void showContainer(BoostContainerSettings settings) {
  if (settings.uniqueId == _onstage.settings.uniqueId) {
   _onShownContainerChanged(null, settings.uniqueId);
   return;
  }

  final int index = _offstage.indexWhere((BoostContainer container) =>
    container.settings.uniqueId == settings.uniqueId);
  if (index > -1) {
   _offstage.add(_onstage);
   _onstage = _offstage.removeAt(index);
   setState(() {});
  } else {
   pushContainer(settings);//main
  }
 }
}

pushContainer

void pushContainer(BoostContainerSettings settings) {
  assert(settings.uniqueId != _onstage.settings.uniqueId);
  assert(_offstage.every((BoostContainer container) => container.settings.uniqueId != settings.uniqueId));

  _offstage.add(_onstage);
  _onstage = BoostContainer.obtain(widget.initNavigator, settings);//main

  setState(() {});
 }

BoostContainer.obtain

BoostContainer extends Navigator {
  final BoostContainerSettings settings;
  @override
  BoostContainerState createState() => BoostContainerState();

  @override
  StatefulElement createElement() => ContainerElement(this);
  
  factory BoostContainer.obtain(
     Navigator navigator, BoostContainerSettings settings) =>
   BoostContainer(
     key: GlobalKey<BoostContainerState>(),
     settings: settings,
     onGenerateRoute: (RouteSettings routeSettings) {//main
      if (routeSettings.name == '/') {
       return BoostPageRoute<dynamic>(//main
         pageName: settings.name,
         params: settings.params,
         uniqueId: settings.uniqueId,
         animated: false,
         settings: routeSettings,
         builder: settings.builder);//main
      } else {
       return navigator.onGenerateRoute(routeSettings);
      }
     },
     observers: <NavigatorObserver>[
      ContainerNavigatorObserver.bindContainerManager()
     ],
     onUnknownRoute: navigator.onUnknownRoute);
}

BoostContainerState extends NavigatorState 

ContainerManagerState.setState

@override
 void setState(VoidCallback fn) {
  if (SchedulerBinding.instance.schedulerPhase ==
    SchedulerPhase.persistentCallbacks) {
   SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
    _refreshOverlayEntries();
   });
  } else {
   _refreshOverlayEntries();//main
  }

  fn();
  //return super.setState(fn);
 }
void _refreshOverlayEntries() {
  final OverlayState overlayState = _overlayKey.currentState;

  final List<BoostContainer> containers = <BoostContainer>[];
   containers.addAll(_offstage);

  assert(_onstage != null, 'Should have a least one BoostContainer');
  containers.add(_onstage);
 
  _leastEntries = containers
   .map<_ContainerOverlayEntry>(
     (BoostContainer container) => _ContainerOverlayEntry(container))
   .toList(growable: false);
 
 overlayState.insertAll(_leastEntries);//main

SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
  final String now = _onstage.settings.uniqueId;
  if (_lastShownContainer != now) {
   final String old = _lastShownContainer;
   _lastShownContainer = now;
   _onShownContainerChanged(old, now);//main
  }
  updateFocuse();
 });
void _onShownContainerChanged(String old, String now) {
  FlutterBoost.singleton.channel.invokeMethod('onShownContainerChanged',properties);
 }

BoostPageRoute

BoostPageRoute<T> extends MaterialPageRoute<T> {
  final String pageName;
 final String uniqueId;
 final Map params;
 final bool animated;
 final WidgetBuilder builder;
 final RouteSettings settings;

static BoostPageRoute<T> of<T>(BuildContext context) {
  final Route<T> route = ModalRoute.of(context);
  if (route != null && route is BoostPageRoute<T>) {
   return route;
  } else {
   throw Exception('not in a BoostPageRoute');
  }
 }

BoostPageRoute(
   {Key stubKey,
   this.pageName,
   this.params,
   this.uniqueId,
   this.animated,
   this.builder,
   this.settings})
   : super(
      builder: (BuildContext context) => Stub(stubKey, builder(context)),
      settings: settings);

@immutable
 class Stub extends StatefulWidget {
  final Widget child;

  const Stub(Key key, this.child) : super(key: key);

  @override
  _StubState createState() => _StubState();
 }

 class _StubState extends State<Stub> {
  @override
  Widget build(BuildContext context) => widget.child;
 }
}

main.dart

main

void main() {
  runApp(MyApp());
 }

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
 }

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
   super.initState();
 
   FlutterBoost.singleton.registerPageBuilders({//main
    'embeded': (pageName, params, _)=>EmbededFirstRouteWidget(),
    'first': (pageName, params, _) => FirstRouteWidget(),
    'second': (pageName, params, _) => SecondRouteWidget(),
    'tab': (pageName, params, _) => TabRouteWidget(),
    'platformView': (pageName, params, _) => PlatformRouteWidget(),
    'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),
    ///可以在native层通过 getContainerParams 来传递参数
    'flutterPage': (pageName, params, _) {
     print("flutterPage params:$params");
     return FlutterRouteWidget(params:params);
    },
   });
  }

@override
 Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Boost example',
    builder: FlutterBoost.init(postPush: _onRoutePushed),//main
    home: Container());
 }
 
 void _onRoutePushed(
   String pageName, String uniqueId, Map params, Route route, Future _) {
 }

registerPageBuilders

FlutterBoost

typedef Widget PageBuilder(String pageName, Map params, String uniqueId);

///Register a map builders
 void registerPageBuilders(Map<String, PageBuilder> builders) {
  ContainerCoordinator.singleton.registerPageBuilders(builders);
 }

ContainerCoordinator

void registerPageBuilders(Map<String, PageBuilder> builders) {
  if (builders?.isNotEmpty == true) {
   _pageBuilders.addAll(builders);//main
  }
 }

init

FlutterBoost

static TransitionBuilder init(
   {TransitionBuilder builder,
   PrePushRoute prePush,
   PostPushRoute postPush}) {

  if (Platform.isAndroid) {
    onPageStart();
   }

  return (BuildContext context, Widget child) {
  assert(child is Navigator, 'child must be Navigator, what is wrong?');
 
  final BoostContainerManager manager = BoostContainerManager(
    key: _instance.containerManagerKey,
    initNavigator: child,
    prePushRoute: prePush,
    postPushRoute: postPush);
 
  if (builder != null) {
   return builder(context, manager);
  } else {
   return manager;
  }
 };

总结

  1. 初始化engine,调用dart main方法,注册页面url和widget的映射关系
  2. startActivity前在intent里配置页面url
  3. container_manager控制showContainer重绘,重新build时选择新的route
  4. 为什么不使用官方的routes+Navigator控制当前的flutter页面?

参考

FlutterBoost

open

Future<Map<dynamic, dynamic>> open(String url,
   {Map<dynamic, dynamic> urlParams, Map<dynamic, dynamic> exts}) {
  Map<dynamic, dynamic> properties = new Map<dynamic, dynamic>();
  properties["url"] = url;
  properties["urlParams"] = urlParams;
  properties["exts"] = exts;
  return channel.invokeMethod<Map<dynamic, dynamic>>('openPage', properties);
 }

FlutterEngine

/* <p>
 * To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a
 * {@link FlutterRenderer} and then attach a {@link RenderSurface}. Consider using a
 * {@link io.flutter.embedding.android.FlutterView} as a {@link RenderSurface}.*/
FlutterEngine

/**
 \* The rendering system associated with this {@code FlutterEngine}.
 \* <p>
 \* To render a Flutter UI that is produced by this {@code FlutterEngine}'s Dart code, attach
 \* a {@link RenderSurface} to this
 \* {@link FlutterRenderer}.
 */
 @NonNull
 public FlutterRenderer getRenderer() {
  return renderer;
 }

FlutterRenderer

private final FlutterJNI flutterJNI;

startRenderingToSurface

/**
 \* Notifies Flutter that the given {@code surface} was created and is available for Flutter
 \* rendering.
 \* <p>
 \* See {@link android.view.SurfaceHolder.Callback} and
 \* {@link android.view.TextureView.SurfaceTextureListener}
 */
 public void startRenderingToSurface(@NonNull Surface surface) {
  if (this.surface != null) {
   stopRenderingToSurface();
  }

  this.surface = surface;

  flutterJNI.onSurfaceCreated(surface);
 }

RenderSurface

/**
 \* Owns a {@code Surface} that {@code FlutterRenderer} would like to paint.
 \* <p>
 \* {@code RenderSurface} is responsible for providing a {@code Surface} to a given
 \* {@code FlutterRenderer} when requested, and then notify that {@code FlutterRenderer} when
 \* the {@code Surface} changes, or is destroyed.
 \* <p>
 \* The behavior of providing a {@code Surface} is delegated to this interface because the timing
 \* of a {@code Surface}'s availability is determined by Android. Therefore, an accessor method
 \* would not fulfill the requirements. Therefore, a {@code RenderSurface} is given a
 \* {@code FlutterRenderer}, which the {@code RenderSurface} is expected to notify as a
 \* {@code Surface} becomes available, changes, or is destroyed.
 */

RenderSurface {
  /**
 \* Returns the {@code FlutterRenderer} that is attached to this {@code RenderSurface}, or
 \* null if no {@code FlutterRenderer} is currently attached.
 */
 @Nullable
 FlutterRenderer getAttachedRenderer();

/**
 \* Instructs this {@code RenderSurface} to give its {@code Surface} to the given
 \* {@code FlutterRenderer} so that Flutter can paint pixels on it.
 \* <p>
 \* After this call, {@code RenderSurface} is expected to invoke the following methods on
 \* {@link FlutterRenderer} at the appropriate times:
 \* <ol>
 \*  <li>{@link FlutterRenderer#startRenderingToSurface(Surface)}</li>
 \*  <li>{@link FlutterRenderer#surfaceChanged(int, int)}}</li>
 \*  <li>{@link FlutterRenderer#stopRenderingToSurface()}</li>
 \* </ol>
 */
 void attachToRenderer(@NonNull FlutterRenderer renderer);
}

XFlutterView

attachToFlutterEngine

public void attachToFlutterEngine(
     @NonNull FlutterEngine flutterEngine
 ) {

Log.d(TAG, "Attaching to a FlutterEngine: " + flutterEngine);
 if (isAttachedToFlutterEngine()) {
  if (flutterEngine == this.flutterEngine) {
   // We are already attached to this FlutterEngine
   Log.d(TAG, "Already attached to this engine. Doing nothing.");
   return;
  }
 
  // Detach from a previous FlutterEngine so we can attach to this new one.
  Log.d(TAG, "Currently attached to a different engine. Detaching and then attaching"
      \+ " to new engine.");
  detachFromFlutterEngine();
 }

this.flutterEngine = flutterEngine;

// Instruct our FlutterRenderer that we are now its designated RenderSurface.
 FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();

flutterRenderer.attachToRenderSurface(renderSurface);

ContainerManagerState

_ContainerOverlayEntry

ContainerManagerState extends State<BoostContainerManager> {
  class _ContainerOverlayEntry extends OverlayEntry {
  bool _removed = false;
  _ContainerOverlayEntry(BoostContainer container)
    : super(
       builder: (BuildContext ctx) => container,
       opaque: true,
       maintainState: true);
       
 @override
 Widget build(BuildContext context) {
  return Overlay(
   key: _overlayKey,
   initialEntries: const <OverlayEntry>[],
  );
 }
}

providePlatformPlugin

public PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
   if (activity != null) {
     return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
   }
}