preface

FlutterApplicaiton and FlutterActivity are analyzed in the previous article Flutter Android startup source code analysis (A). In FlutterActivity source part FlutterActivityAndFragmentDelegate this class has been mentioned, this article we mainly analyze the are doing it

Flutter Android startup

FlutterActivityAndFragmentDelegate

  • role
    • Delegate of the same Flutter logic between FlutterActivity and FlutterFragment
  • Why use this class
    • Given that you can place a Fragment in an Activity, it makes more sense to use a FlutterFragment in a FlutterActivity. The Fragment support library adds 100K binary sizes to applications, while full Flutter applications do not require binary hits. Therefore, it is concluded that Flutter must be based on AOSP(Android Open Source Project: Android open source project Activity provides FlutterActivity and a separate FlutterFragment for developers adding applications
    • FlutterActivity and FlutterFragment are independent of each other, so a proxy-delegate class is required to handle the same logic between them
Member variables
// Host implementation object FlutterActivity @nonnull private Host Host; // Flutter implementation environment @nullable private FlutterEngine FlutterEngine; @nullable private FlutterSplashView FlutterSplashView; // Flutter UI @Nullable private FlutterView flutterView; @nullable private PlatformPlugin plugin; FlutterEngine private Boolean isFlutterEngineFromHost;Copy the code
The constructor
 FlutterActivityAndFragmentDelegate(@NonNull Host host) {
    this.host = host;
  }
Copy the code

In the onCreate method FlutterActivity instantiation FlutterActivityAndFragmentDelegate and pass the current FA context object,

Then call onAttach, onActivityCreated, and onCreateView. Let’s analyze these three methods one by one

@override protected void onCreate(@nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); delegate = new FlutterActivityAndFragmentDelegate(this); delegate.onAttach(this); delegate.onActivityCreated(savedInstanceState); setContentView(createFlutterView()); } @NonNull private View createFlutterView() { return delegate.onCreateView( null /* inflater */, null /* container */, null /* savedInstanceState */); }Copy the code
onAttach
 void onAttach(@NonNull Context context) {
    ensureAlive();
    if (flutterEngine == null) {
    	// 1
      setupFlutterEngine();
    }
    // 2
    platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
    // 3 
    if (host.shouldAttachEngineToActivity()) {
      flutterEngine
          .getActivityControlSurface()
          .attachToActivity(host.getActivity(), host.getLifecycle());
    }
    // 4 
    host.configureFlutterEngine(flutterEngine);
  }
Copy the code
  1. Instantiation FlutterEngine

    void setupFlutterEngine() { String cachedEngineId = host.getCachedEngineId(); if (cachedEngineId ! = null) { flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId); isFlutterEngineFromHost = true; if (flutterEngine == null) { throw new IllegalStateException( "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '" + cachedEngineId + "'"); } return; } flutterEngine = host.provideFlutterEngine(host.getContext()); if (flutterEngine ! = null) { isFlutterEngineFromHost = true; return; } flutterEngine = new FlutterEngine( host.getContext(), host.getFlutterShellArgs().toArray(), /*automaticallyRegisterPlugins=*/ false, /*willProvideRestorationData=*/ host.shouldRestoreAndSaveState()); isFlutterEngineFromHost = false; }Copy the code

    First, it checks whether there is a cached EngineId. If there is a cached EngineId, it gets it from the FlutterEngineCache cache and instantiates it.

    If FlutterActivity does not provide an Engine, the Host implementation class will be checked.

    	// FlutterActivity
    	@Nullable
      @Override
      public FlutterEngine provideFlutterEngine(@NonNull Context context) {
        // No-op. Hook for subclasses.
        return null;
      }
    Copy the code

    A new Engine is instantiated directly and isFlutterEngineFromHost is set to false, indicating that the Engine is not provided by the Host implementation class

    Let’s look briefly at the constructor of FlutterEngine

    public FlutterEngine( @NonNull Context context, @NonNull FlutterLoader flutterLoader, @NonNull FlutterJNI flutterJNI, @NonNull PlatformViewsController platformViewsController, @Nullable String[] dartVmArgs, boolean automaticallyRegisterPlugins, boolean waitForRestorationData) { this.dartExecutor = new DartExecutor(flutterJNI, context.getAssets()); this.dartExecutor.onAttachedToJNI(); accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI); keyEventChannel = new KeyEventChannel(dartExecutor); lifecycleChannel = new LifecycleChannel(dartExecutor); localizationChannel = new LocalizationChannel(dartExecutor); mouseCursorChannel = new MouseCursorChannel(dartExecutor); navigationChannel = new NavigationChannel(dartExecutor); platformChannel = new PlatformChannel(dartExecutor); restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData); settingsChannel = new SettingsChannel(dartExecutor); systemChannel = new SystemChannel(dartExecutor); textInputChannel = new TextInputChannel(dartExecutor); this.localizationPlugin = new LocalizationPlugin(context, localizationChannel); this.flutterJNI = flutterJNI; flutterLoader.startInitialization(context.getApplicationContext()); flutterLoader.ensureInitializationComplete(context, dartVmArgs); flutterJNI.addEngineLifecycleListener(engineLifecycleListener); flutterJNI.setPlatformViewsController(platformViewsController); flutterJNI.setLocalizationPlugin(localizationPlugin); attachToJni(); // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if // possible. this.renderer = new FlutterRenderer(flutterJNI); this.platformViewsController = platformViewsController; this.platformViewsController.onAttachedToJNI(); this.pluginRegistry = new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader); if (automaticallyRegisterPlugins) { registerPlugins(); }}Copy the code
    • DartExecutor instantiation
    • Some system channels are initialized
    • FlutterJNI, PlatformViewsController, FlutterRender and FlutterLoader are initialized
    • Instantiation FlutterEnginePluginRegistry plug-in registry

    FlutterRender works in conjunction with the provided RenderSurface to draw Flutter pixels into the Android View hierarchy. FlutterRender manages render textures and forwards some Java calls to local Flutter code via JNI

    The FlutterLoader is the local library of a Flutter that looks up Flutter resources in the application APK and loads Flutter

    These two we can go to have a look, not to mention

  2. Instantiation PlatformPlugin

     // FlutterActivity
     @Nullable
      @Override
      public PlatformPlugin providePlatformPlugin(
          @Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
        if (activity != null) {
          return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
        } else {
          return null;
        }
      }
    Copy the code

    You can see that FlutterActivity overrides this method and instantiates a new PlatformPlugin

    One caveat: Whenever FlutterFragment connects to a new Activity, a PlatformPlugin needs to be created again

    PlatformPlugin is a native Plugin provided by the platform, such as status bar operation and clipboard Plugin

  3. attachToActivity

     // FlutterActivity
     @Override
      public boolean shouldAttachEngineToActivity() {
        return true;
      }
    Copy the code

    The default is to return true to establish an association between the Activity and the Engine

    The attachToActivity method notifies all plug-ins currently connected to the FlutterEngine to attach them to the Activity

    / / FlutterEnginePluginRegistry plug-in registry @ Override public void attachToActivity (@ NonNull Activity to Activity, @NonNull Lifecycle lifecycle) { detachFromAndroidComponent(); this.activity = activity; this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity, lifecycle); flutterEngine .getPlatformViewsController() .attach(activity, flutterEngine.getRenderer(), flutterEngine.getDartExecutor()); for (ActivityAware activityAware : activityAwarePlugins.values()) { if (isWaitingForActivityReattachment) { activityAware.onReattachedToActivityForConfigChanges(activityPluginBinding); } else { activityAware.onAttachedToActivity(activityPluginBinding); } } isWaitingForActivityReattachment = false; }Copy the code

    FlutterEnginePluginRegistry you are interested in this class may have a look, is actually a Engine plug-in registry, don’t do too much analysis here

    It is instantiated in the constructor of FlutterEngine

  4. configureFlutterEngine

    // FlutterActivity
    @Override
      public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        registerPlugins(flutterEngine);
      }
    Copy the code

    Register native plug-ins in Pubspec.yaml, so as mentioned in the previous article, the new version helps us with automatic registration.

onActivityCreated
	void onActivityCreated(@Nullable Bundle bundle) {
    Bundle pluginState = null;
    byte[] frameworkState = null;
    if (bundle != null) {
      pluginState = bundle.getBundle(PLUGINS_RESTORATION_BUNDLE_KEY);
      frameworkState = bundle.getByteArray(FRAMEWORK_RESTORATION_BUNDLE_KEY);
    }
    if (host.shouldRestoreAndSaveState()) {
      flutterEngine.getRestorationChannel().setRestorationData(frameworkState);
    }
    if (host.shouldAttachEngineToActivity()) {
      flutterEngine.getActivityControlSurface().onRestoreInstanceState(pluginState);
    }
  }
Copy the code

We won’t do too much analysis of this, but provide recovery data for plug-ins and activities

onCreateView
View onCreateView(
      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    ensureAlive();
    if (host.getRenderMode() == RenderMode.surface) {
      FlutterSurfaceView flutterSurfaceView =
          new FlutterSurfaceView(
              host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent);
      host.onFlutterSurfaceViewCreated(flutterSurfaceView);
      flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
    } else {
      FlutterTextureView flutterTextureView = new FlutterTextureView(host.getActivity());
      host.onFlutterTextureViewCreated(flutterTextureView);
      flutterView = new FlutterView(host.getActivity(), flutterTextureView);
    }
    flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
    flutterSplashView = new FlutterSplashView(host.getContext());
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      flutterSplashView.setId(View.generateViewId());
    } else {
      flutterSplashView.setId(486947586);
    }
    flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
    flutterView.attachToFlutterEngine(flutterEngine);
    return flutterSplashView;
  }
Copy the code
  1. The render mode is judged first

    // FlutterActivity @NonNull @Override public RenderMode getRenderMode() { return getBackgroundMode() == BackgroundMode.opaque ? RenderMode.surface : RenderMode.texture; } @NonNull protected BackgroundMode getBackgroundMode() { if (getIntent().hasExtra(EXTRA_BACKGROUND_MODE)) { return BackgroundMode.valueOf(getIntent().getStringExtra(EXTRA_BACKGROUND_MODE)); } else { return BackgroundMode.opaque; }}Copy the code

    Rendermode. surface and rendermode. texture. The Flutter framework uses surface to render by default

  2. Then instantiate an opaque FlutterSurfaceView, and set the onFlutterSurfaceViewCreated callback

    	public enum BackgroundMode {
        /** Indicates a FlutterActivity with an opaque background. This is the default. */
        opaque,
        /** Indicates a FlutterActivity with a transparent background. */
        transparent
      }
    Copy the code
  3. Then create a FlutterView with the FlutterSurfaceView that is used to display the Flutter UI on Android devices

     flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
    Copy the code

    TextureView is the same, not analyzed

  4. A listener is then added to draw the first frame

     flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
    Copy the code
  5. Then will create a FlutterSplashView, then call displayFlutterViewWithSplash method displayed on the FlutterView splash screen interface, until made the first frame

    flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen()); //FlutterActivity @Nullable @Override public SplashScreen provideSplashScreen() { Drawable manifestSplashDrawable = getSplashScreenFromManifest(); if (manifestSplashDrawable ! = null) { return new DrawableSplashScreen(manifestSplashDrawable); } else { return null; } } @Nullable @SuppressWarnings("deprecation") private Drawable getSplashScreenFromManifest() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); Bundle metadata = activityInfo.metaData; int splashScreenId = metadata ! = null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : 0; return splashScreenId ! = 0? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? getResources().getDrawable(splashScreenId, getTheme()) : getResources().getDrawable(splashScreenId) : null; } catch (PackageManager.NameNotFoundException e) { // This is never expected to happen. return null; } } // FlutterActivityLaunchConfigs static final String SPLASH_SCREEN_META_DATA_KEY ="io.flutter.embedding.android.SplashScreenDrawable"; // AndroidManifest.xml <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background" />Copy the code

    You can see that FlutterActivity overwrites provideSplashScreen to return the splash screen drawable we set in Androidmanifest.xml

  6. flutterView.attachToFlutterEngine(flutterEngine);

    This FlutterView is then wired to the given FlutterEngine. This FlutterView will begin rendering the UI drawn by the given FlutterEngine. And the FlutterView will also start forwarding interaction events from the FlutterView to the given FlutterEngine e.g. user touch events, accessibility events, keyboard events, etc

  7. Return flutterSplashView to FlutterActivity. Until now, FlutterActivityAndFragmentDelegate onAttach method has been analyzed completely

  8. SplashScreenDrawable can also be added to solve android’s problem of black screen startup — the setContentView of MainActivity will not be closed until it receives a callback from the first frame of the Flutter drawing

Next, let’s examine the onStart method

onStart
 // FlutterActivity
 @Override
  protected void onStart() {
    super.onStart();
    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
    delegate.onStart();
  }
Copy the code
// FlutterActivityAndFragmentDelegate
void onStart() {
    Log.v(TAG, "onStart()");
    ensureAlive();
    doInitialFlutterViewRun();
  }
Copy the code

Can see FlutterActivity. OnStart calls the FlutterActivityAndFragmentDelegate. OnStart method, then the call doInitialFlutterViewRun method

This method is the first time Dart has been run in FlutterView

private void doInitialFlutterViewRun() { // 1 if (host.getCachedEngineId() ! = null) { return; } if (flutterEngine.getDartExecutor().isExecutingDart()) { return; } // 2 if (host.getInitialRoute() ! = null) { flutterEngine.getNavigationChannel().setInitialRoute(host.getInitialRoute()); } // 3 DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint( host.getAppBundlePath(), host.getDartEntrypointFunctionName()); flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint); }Copy the code
  1. First check whether the engine cache ID is empty, which is implemented in FlutterActivity

    	// FlutterActivity
    	@Override
      @Nullable
      public String getCachedEngineId() {
        return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_ID);
      }
    Copy the code

    To clarify, the Flutter framework instantiates a new FlutterEngine each time, as mentioned above, so it is always null. Specific usage scenarios will be discussed later

    The Dart is then determined to be executed

  2. The initial path to receive the Flutter application before executing the Dart code is set to ensure that the initial path is applied in a timely manner.

    //FlutterActivity @NonNull public String getInitialRoute() { if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) { return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE); } try { ActivityInfo activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); Bundle metadata = activityInfo.metaData; String desiredInitialRoute = metadata ! = null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null; return desiredInitialRoute ! = null ? desiredInitialRoute : DEFAULT_INITIAL_ROUTE; } catch (PackageManager.NameNotFoundException e) { return DEFAULT_INITIAL_ROUTE; }}Copy the code

    The initialization path can be set in the Intent or in the Android Manifest. Definition specifies INITIAL_ROUTE_META_DATA_KEY

    We won’t talk too much about it here, but this finally returns DEFAULT_INITIAL_ROUTE, the default path

    //FlutterActivityLaunchConfigs
    static final String DEFAULT_INITIAL_ROUTE = "/";
    Copy the code
  3. Then configure the Dart code to execute the entry point and execute it

    Host. getAppBundlePath returns the default path

    private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets";
    Copy the code

    Host. GetDartEntrypointFunctionName () returns is also the default startup method name,

    static final String DEFAULT_DART_ENTRYPOINT = "main";
    Copy the code

    Dart is our main method

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    Copy the code
    //DartExecutor configuration, boot and start executing Dart code. public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) { if (isApplicationRunning) { Log.w(TAG, "Attempted to run a DartExecutor that is already running."); return; } Log.v(TAG, "Executing Dart entrypoint: " + dartEntrypoint); flutterJNI.runBundleAndSnapshotFromLibrary( dartEntrypoint.pathToBundle, dartEntrypoint.dartEntrypointFunctionName, null, assetManager); isApplicationRunning = true; }Copy the code

    You can see that isApplicationRunning = true is set to indicate that the program is running,

    //DartExecutor 
    public boolean isExecutingDart() {
        return isApplicationRunning;
      }
    Copy the code

    This variable is also used to determine whether the DART code is executing

  4. The FlutterJNI class is the interface between the Java code embedded with Flutter and the C/C ++ code of the Flutter engine. Go and see for yourself

  5. At this point, a Flutter program is up and running, followed by executing the Dart code and our own business logic

Next, let’s look at the LifecycleChannel LifecycleChannel

 void onResume() {
    Log.v(TAG, "onResume()");
    ensureAlive();
    flutterEngine.getLifecycleChannel().appIsResumed();
  }
Copy the code

It mainly sends Android life cycle events to Flutter, as do onPause, onStop, and onDetach

FlutterActivity onDestroy
  @Override
  protected void onDestroy() {
    super.onDestroy();
    delegate.onDestroyView();
    delegate.onDetach();
    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
  }
Copy the code

Here is mainly to do some clear reference, destroy some operations of resources. You can read the source code

conclusion

Through this article we can see roughly

  • FlutterActivityAndFragmentDelegate FlutterActivity work is how to the agent

  • How FlutterActivityAndFragmentDelegate FlutterEngine and FlutterActivity together, and implement the dart code, start the application

  • FlutterRender, FlutterJNI, FlutterLoader you can look at the source code, the basic startup process is like this

  • On Android, Flutter is loaded into an embed by default as an Activity. The view is controlled by FlutterView, which renders the Flutter contents as views or textures according to their composition and Z-ordering requirements.

  • Review of Flutter architecture

In the next article, I will use an Android project to embed the Flutter Module hybrid project to give you a more intuitive view of how Flutter is represented on Android

My technical level is limited, too in-depth analysis will not involve. If there are any mistakes, please correct them promptly. Of course, you are welcome to give me some guidance, we progress together