This article mainly analyzes the startup process of Flutter Android, mainly the upper-layer code analysis, not the underlying C/C ++ analysis. The SDK for different Flutter versions will also have different code, but the overall flow and principles will not be too different.

1, FlutterApplication

When an Android app is started, it must first initialize the Application and then load the default first class MainActivity. Application specifies FlutterApplication by default for the Android application corresponding to the Flutter project.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.flutter_share">
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="flutter_share"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
Copy the code

1.1 FlutterMain. StartInitialization (this);

The onCreate core initialization code in FlutterApplication has only one line

        FlutterMain.startInitialization(this);
Copy the code

The FlutterMain class is in the flutter. Jar package. Different platforms correspond to different flutter. Jar files in the flutter SDK path: flutterSdkPath\flutter\bin\cache\artifacts\engine.

Now look at the code in FlutterMain

    public static void startInitialization(@NonNull Context applicationContext) {
        if(! isRunningInRobolectricTest) { startInitialization(applicationContext, new io.flutter.view.FlutterMain.Settings()); } } public static void startInitialization(@NonNull Context applicationContext, @NonNull io.flutter.view.FlutterMain.Settings settings) {if(! isRunningInRobolectricTest) {if(Looper.myLooper() ! = Looper.getMainLooper()) { throw new IllegalStateException("startInitialization must be called on the main thread");
            } else if (sSettings == null) {
                sSettings = settings;
                long initStartTimestampMillis = SystemClock.uptimeMillis();
                initConfig(applicationContext);
                initResources(applicationContext);
                System.loadLibrary("flutter");
                VsyncWaiter.getInstance((WindowManager)applicationContext.getSystemService("window")).init(); long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis; FlutterJNI.nativeRecordStartTimestamp(initTimeMillis); }}}Copy the code

We mainly did the following things:

  • The startInitialization function must be executed on the main thread or an exception will be thrown.
  • The sSettings variable is only initialized once.
  • InitConfig Indicates the initial configuration.
  • InitResources Initializes resources.
  • System.loadlibrary (“flutter”) loads the flutter core library libflutter. This library is also in flutter. Jar, which is copied into APK when a Flutter project is compiled.
  • FlutterJNI nativeRecordStartTimestamp mainly is calling the underlying method records the initialization time.

1.2 initConfig

This method is as follows:

    private static void initConfig(@NonNull Context applicationContext) {
        Bundle metadata = getApplicationInfo(applicationContext).metaData;
        if(metadata ! = null) { sAotSharedLibraryName = metadata.getString(PUBLIC_AOT_SHARED_LIBRARY_NAME,"libapp.so");
            sFlutterAssetsDir = metadata.getString(PUBLIC_FLUTTER_ASSETS_DIR_KEY, "flutter_assets");
            sVmSnapshotData = metadata.getString(PUBLIC_VM_SNAPSHOT_DATA_KEY, "vm_snapshot_data");
            sIsolateSnapshotData = metadata.getString(PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY, "isolate_snapshot_data"); }}Copy the code

Parsing metadata initializes the default configuration. If no default value is set, the default value is used.

  • sAotSharedLibraryName : libapp.so
  • sFlutterAssetsDir: flutter_assets
  • sVmSnapshotData:vm_snapshot_data
  • sIsolateSnapshotData:isolate_snapshot_data

1.3 initResources

The code is as follows:

private static void initResources(@NonNull Context applicationContext) { (new ResourceCleaner(applicationContext)).start(); String dataDirPath = PathUtils.getDataDirectory(applicationContext); String packageName = applicationContext.getPackageName(); PackageManager packageManager = applicationContext.getPackageManager(); AssetManager assetManager = applicationContext.getResources().getAssets(); sResourceExtractor = new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager); sResourceExtractor.addResource(fromFlutterAssets(sVmSnapshotData)).addResource(fromFlutterAssets(sIsolateSnapshotData)). addResource(fromFlutterAssets("kernel_blob.bin"));
        sResourceExtractor.start();
    }

Copy the code

There are two main functions:

  • Clear cached files using ResourceCleaner
  • The resource files in the specified directory are loaded through sRecourceExtractor. The flutter Engine and Darv Vm are initialized through these files.

This is the Application initialization process. Next look at the execution of MainActivity.

2, MainActivity

The MainActivity class is automatically generated in the Flutter project created by Android. The code for this class is as follows:

public class MainActivity extends FlutterActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); }}Copy the code

As you can see, MainActivity inherits FlutterActivity from Flutter’s own implementation.

2.1 GeneratedPluginRegistrant. RegisterWith (this)

The core code in MainActivity is just one line

GeneratedPluginRegistrant.registerWith(this);
Copy the code

GeneratedPluginRegistrant this class code is as follows:

/**
 * Generated file. Do not edit.
 */
public final class GeneratedPluginRegistrant {
  public static void registerWith(PluginRegistry registry) {
    if (alreadyRegisteredWith(registry)) {
      return;
    }
  }

  private static boolean alreadyRegisteredWith(PluginRegistry registry) {
    final String key = GeneratedPluginRegistrant.class.getCanonicalName();
    if (registry.hasPlugin(key)) {
      return true;
    }
    registry.registrarFor(key);
    return false; }}Copy the code

As you can see, this class is automatically generated and executes the registry.registrarfor (key) method. Registry is an interface, and FlutterActivity implements this interface. MainActivity also inherits from FlutterActivity. Next, analyze FlutterActivity.

3, FlutterActivity

public class FlutterActivity extends Activity implements Provider, PluginRegistry, ViewFactory 
Copy the code

Can see FlutterActivity implements the Provider, PluginRegistry ViewFactory three interfaces.

    private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
    private final FlutterActivityEvents eventDelegate;
    private final Provider viewProvider;
    private final PluginRegistry pluginRegistry;

    public FlutterActivity() {
        this.eventDelegate = this.delegate;
        this.viewProvider = this.delegate;
        this.pluginRegistry = this.delegate;
    }
Copy the code

As you can see, all three references refer to the same object, meaning that this delegate object will implement the functions declared by the other three classes.

Moving on to the main methods in FlutterActivity:

    protected void onStart() {
        super.onStart();
        this.eventDelegate.onStart();
    }

    protected void onResume() { super.onResume(); this.eventDelegate.onResume(); }...Copy the code

It can be seen that the core method in FlutterActivity simply calls back the corresponding method in the delegate, that is, the class FlutterActivity is only a proxy, and the class corresponding to the delegate object must be the one that really implements the function. FlutterActivityDelegate.

4、 FlutterActivityDelegate

public final class FlutterActivityDelegate implements FlutterActivityEvents, Provider, PluginRegistry 
Copy the code

This class is not inherited by final, and it does implement three interfaces.

4.1 onCreate

    public void onCreate(Bundle savedInstanceState) {
        if (VERSION.SDK_INT >= 21) {
            Window window = this.activity.getWindow();
            window.addFlags(-2147483648);
            window.setStatusBarColor(1073741824);
            window.getDecorView().setSystemUiVisibility(1280);
        }

        String[] args = getArgsFromIntent(this.activity.getIntent());
        FlutterMain.ensureInitializationComplete(this.activity.getApplicationContext(), args);
        this.flutterView = this.viewFactory.createFlutterView(this.activity);
        if (this.flutterView == null) {
            FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView();
            this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView);
            this.flutterView.setLayoutParams(matchParent);
            this.activity.setContentView(this.flutterView);
            this.launchView = this.createLaunchView();
            if (this.launchView != null) {
                this.addLaunchView();
            }
        }

        if(! this.loadIntent(this.activity.getIntent())) { String appBundlePath = FlutterMain.findAppBundlePath();if(appBundlePath ! = null) { this.runBundle(appBundlePath); }}}Copy the code

OnCreate is the first function executed after this class is initialized.

  • Set up an immersive status bar
  • Gets parameters from the intent
  • Call FlutterMain ensureInitializationComplete ensure resource initialization is complete.
  • Create FlutterNativeView and FlutterView object instances.
  • Set the view layout of the current activity to flutterView.
  • The runBundle will load the Dart code with a series of method calls.

4.1 FlutterMain.ensureInitializationComplete

  public static void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
        if(! isRunningInRobolectricTest) {if(Looper.myLooper() ! = Looper.getMainLooper()) { throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
            } else if (sSettings == null) {
                throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
            } else if(! sInitialized) { try {if(sResourceExtractor ! = null) { sResourceExtractor.waitForCompletion(); } List<String> shellArgs = new ArrayList(); shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
                    ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
                    shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + "libflutter.so");
                    if(args ! = null) { Collections.addAll(shellArgs, args); } String kernelPath = null; shellArgs.add("--aot-shared-library-name=" + sAotSharedLibraryName);
                    shellArgs.add("--aot-shared-library-name=" + applicationInfo.nativeLibraryDir + File.separator + sAotSharedLibraryName);
                    shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext));
                    if(sSettings.getLogTag() ! = null) { shellArgs.add("--log-tag=" + sSettings.getLogTag());
                    }

                    String appStoragePath = PathUtils.getFilesDir(applicationContext);
                    String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
                    FlutterJNI.nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), (String)kernelPath, appStoragePath, engineCachesPath);
                    sInitialized = true;
                } catch (Exception var7) {
                    Log.e("FlutterMain"."Flutter initialization failed.", var7); throw new RuntimeException(var7); }}}}Copy the code

The code in release mode is analyzed here, and its main functions are as follows:

  • Main thread execution
  • SSetting cannot be null, otherwise an exception will be thrown.
  • Ensure that sResourceExtractor resource extraction is complete.
  • Pass all configuration parameters (such as the path of the Flutter core library, the path of the SO library generated after the compilation of the Flutter APP project) to shellArgs, and the List array will be passed to the underlying initialization of the Dart Vm, etc.
  • The underlying initial operation is done through FlutterJNI.

4.2 FlutterView Initialization

public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
    ...
    
    public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
        super(context, attrs);
        this.nextTextureId = new AtomicLong(0L);
        this.mIsSoftwareRenderingEnabled = false;
        this.didRenderFirstFrame = false;
        this.onAccessibilityChangeListener = new OnAccessibilityChangeListener() { public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) { FlutterView.this.resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled); }}; Activity activity = getActivity(this.getContext());if (activity == null) {
            throw new IllegalArgumentException("Bad context");
        } else {
            if (nativeView == null) {
                this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
            } else {
                this.mNativeView = nativeView;
            }

            this.dartExecutor = this.mNativeView.getDartExecutor();
            this.flutterRenderer = new FlutterRenderer(this.mNativeView.getFlutterJNI());
            this.mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled();
            this.mMetrics = new FlutterView.ViewportMetrics();
            this.mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
            this.setFocusable(true);
            this.setFocusableInTouchMode(true);
            this.mNativeView.attachViewAndActivity(this, activity);
            this.mSurfaceCallback = new Callback() { public void surfaceCreated(SurfaceHolder holder) { FlutterView.this.assertAttached(); FlutterView.this.mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface()); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { FlutterView.this.assertAttached(); FlutterView.this.mNativeView.getFlutterJNI().onSurfaceChanged(width, height); } public void surfaceDestroyed(SurfaceHolder holder) { FlutterView.this.assertAttached(); FlutterView.this.mNativeView.getFlutterJNI().onSurfaceDestroyed(); }}; this.getHolder().addCallback(this.mSurfaceCallback); this.mActivityLifecycleListeners = new ArrayList(); this.mFirstFrameListeners = new ArrayList(); this.navigationChannel = new NavigationChannel(this.dartExecutor); this.keyEventChannel = new KeyEventChannel(this.dartExecutor); this.lifecycleChannel = new LifecycleChannel(this.dartExecutor); this.localizationChannel = new LocalizationChannel(this.dartExecutor); this.platformChannel = new PlatformChannel(this.dartExecutor); this.systemChannel = new SystemChannel(this.dartExecutor); this.settingsChannel = new SettingsChannel(this.dartExecutor); final PlatformPlugin platformPlugin = new PlatformPlugin(activity, this.platformChannel); this.addActivityLifecycleListener(newActivityLifecycleListener() {
                public void onPostResume() { platformPlugin.updateSystemUiOverlays(); }}); this.mImm = (InputMethodManager)this.getContext().getSystemService("input_method"); PlatformViewsController platformViewsController = this.mNativeView.getPluginRegistry().getPlatformViewsController(); this.mTextInputPlugin = new TextInputPlugin(this, this.dartExecutor, platformViewsController); this.androidKeyProcessor = new AndroidKeyProcessor(this.keyEventChannel, this.mTextInputPlugin); this.androidTouchProcessor = new AndroidTouchProcessor(this.flutterRenderer); this.mNativeView.getPluginRegistry().getPlatformViewsController().attachTextInputPlugin(this.mTextInputPlugin); this.sendLocalesToDart(this.getResources().getConfiguration()); this.sendUserPlatformSettingsToDart(); }}... }Copy the code

As you can see, FlutterView inherited SurfaceView and implements BinaryMessenger TextureRegistry two interfaces.

Main functions:

  • Create FlutterNativeView.
  • Create a CallBack instance to notify the underlying layer when the Surface CallBack is executed.
  • Create platform channels and pass platform Settings information to Dart.

4.3 FlutterNativeView Initialization

public class FlutterNativeView implements BinaryMessenger {
    ...
    public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
        this.flutterUiDisplayListener = new FlutterUiDisplayListener() {
            public void onFlutterUiDisplayed() {
                if(FlutterNativeView.this.mFlutterView ! = null) { FlutterNativeView.this.mFlutterView.onFirstFrame(); } } public voidonFlutterUiNoLongerDisplayed() {}}; this.mContext = context; this.mPluginRegistry = new FlutterPluginRegistry(this, context); this.mFlutterJNI = new FlutterJNI(); this.mFlutterJNI.addIsDisplayingFlutterUiListener(this.flutterUiDisplayListener); this.dartExecutor = new DartExecutor(this.mFlutterJNI, context.getAssets()); this.mFlutterJNI.addEngineLifecycleListener(new FlutterNativeView.EngineLifecycleListenerImpl()); this.attach(this, isBackgroundView); this.assertAttached(); }... }Copy the code
  • Initialize the object FlutterPluginRegistry;
  • Initialize the object FlutterJNI;
  • Initialize the object RenderSurfaceImpl and assign it to the mFlutterJNI member variable renderSurface;
  • Initialize the object DartExecutor;
  • Set the engine lifecycle callback listener;
  • And implement the ATTACH method
    private void attach(FlutterNativeView view, boolean isBackgroundView) {
        this.mFlutterJNI.attachToNative(isBackgroundView);
        this.dartExecutor.onAttachedToJNI();
    }
Copy the code
  @UiThread
    public void attachToNative(boolean isBackgroundView) {
        this.ensureRunningOnMainThread();
        this.ensureNotAttachedToNative();
        this.nativePlatformViewId = this.nativeAttach(this, isBackgroundView);
    }
Copy the code

After the above two functions are executed, the JNI layer methods will be executed. In turn, the underlying code will be executed to initialize and start the Flutter engine.

After the Flutter engine and Dart Vm have been initialized, the code will be executed to the runBundle method in the FlutterActivityDelegate.

4.4 runBundle

    private void runBundle(String appBundlePath) {
        if(! this.flutterView.getFlutterNativeView().isApplicationRunning()) { FlutterRunArguments args = new FlutterRunArguments(); args.bundlePath = appBundlePath; args.entrypoint ="main"; this.flutterView.runFromBundle(args); }}Copy the code

Basically, you create a FlutterRunArguments object, specify the entry function name as “main”, and then execute the runFromBundle method in FlutterView.


    public void runFromBundle(FlutterRunArguments args) {
        this.assertAttached();
        this.preRun();
        this.mNativeView.runFromBundle(args);
        this.postRun();
    }
    
        public void runFromBundle(FlutterRunArguments args) {
        if (args.entrypoint == null) {
            throw new AssertionError("An entrypoint must be specified");
        } else {
            this.assertAttached();
            if (this.applicationIsRunning) {
                throw new AssertionError("This Flutter engine instance is already running an application");
            } else {
                this.mFlutterJNI.runBundleAndSnapshotFromLibrary(args.bundlePath, args.entrypoint, args.libraryPath, this.mContext.getResources().getAssets());
                this.applicationIsRunning = true; }}}Copy the code

In runFromBundle, we further call jNI layer methods, specifying function names, library paths, resource files, and so on.

  @UiThread
    public void runBundleAndSnapshotFromLibrary(@NonNull String bundlePath, @Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction, @NonNull AssetManager assetManager) {
        this.ensureRunningOnMainThread();
        this.ensureAttachedToNative();
        this.nativeRunBundleAndSnapshotFromLibrary(this.nativePlatformViewId, bundlePath, entrypointFunctionName, pathToEntrypointFunction, assetManager);
    }
Copy the code

The underlying method is then called:

    private native void nativeRunBundleAndSnapshotFromLibrary(long var1, @NonNull String var3, @Nullable String var4, @Nullable String var5, @NonNull AssetManager var6);
Copy the code

The underlying method, through a series of method calls, will execute to the main function in Dart, which will run into the Dart code we wrote.

The whole process is illustrated below.

Welcome to follow the wechat public account “Flutter Programming and Development”.