The entire UI framework summary logic and dartVM are packaged into the Flutter. So library. After being developed in Android, the system is packaged into the Asset directory. Dart files under the Asset folder will be extracted into the Android installation package environment for loading and execution. Finally, the relevant interface of FlutterUI will be summarized and rendered in the SurfaceView provided by The Android terminal, and the loading process of Flutter files in Android will be analyzed

Android initialization

Once compiled, the Flutter Engine generates a FlutterJar package for Android to interact with Flutter code

How does the Flutter code load the Dart code when the Flutter code starts up on Android, execute it, and display it on mobile

!!!!!!!!! Info “Android initialization process”

The FlutterMain class initializes Flutter files. See the file structure * 3 in the Apk package of Flutter. * 4.System. LoadLibrary (" Flutter ") loads the 'libflutter. The JNI_OnLoad method is automatically called to associate the Flutter Java platform code with JNI * 6. Call 'Init()' in fluttermain.cc to initialize the flutter code extracted on the Java side and pass the associated file path to JNI for initialization. The file path can be read by the JNI layerCopy the code

Application;

!!!!!!!!! info “STEP”

* 1. Application of call FlutterMain. StartInitialization (this); * 2. Initialize the path and file name of the flutter file to facilitate reading of flutter files in different locations * 3. Extract AOT-optimized code * 4. Load resource files into the resource lookup path * 5. Load so library: system. loadLibrary("flutter"); * 6.FlutterActivity: Initialization of View and event actionsCopy the code

After the android project is packaged, extract the libFlutter. Jar file to the lib folder, resource files and vm and related configuration files to the lib folder. This step is the same file loaded today. After the App is successfully installed, the App will judge the time stamp to determine whether the current file needs to be extracted to the Android directory folder again.

Initialize the Flutter file

Application starts Flutter. Jar for initialization

public class FlutterApplication extends Application {
    private Activity mCurrentActivity = null;

    public FlutterApplication(a) {}@CallSuper
    public void onCreate(a) {
        super.onCreate();
        FlutterMain.startInitialization(this);
    }

    public Activity getCurrentActivity(a) {
        return this.mCurrentActivity;
    }

    public void setCurrentActivity(Activity mCurrentActivity) {
        this.mCurrentActivity = mCurrentActivity; }}Copy the code

Extract the code related to Flutter and load the Flutter. So library

Flutter files in APK need to be loaded to run the DART code

3. System.loadlibrary (“flutter”); 4. Initialize the SO library

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

Initialize the configuration file. After the APK package is installed on the phone, you can find it in the APK directory. Initialize the search path

private void initConfig(@NonNull Context applicationContext) {
    Bundle metadata = this.getApplicationInfo(applicationContext).metaData;
    if(metadata ! =null) {
        this.aotSharedLibraryName = metadata.getString(PUBLIC_AOT_SHARED_LIBRARY_NAME, "libapp.so");
        this.flutterAssetsDir = metadata.getString(PUBLIC_FLUTTER_ASSETS_DIR_KEY, "flutter_assets");
        this.vmSnapshotData = metadata.getString(PUBLIC_VM_SNAPSHOT_DATA_KEY, "vm_snapshot_data");
        this.isolateSnapshotData = metadata.getString(PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY, "isolate_snapshot_data"); }}Copy the code

Apk is a file compression package. The Flutter code is issued in the APK Assert package and needs to be exported and extracted in the APK installation directory. Through the ExtractTask class, the AssetManager manager can extract the relevant flutter code. According to the timestamp file in the directory to determine whether the file has been extracted res_TIMESTAMP

    private 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();
        this.resourceExtractor = new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager);
        this.resourceExtractor.addResource(this.fullAssetPathFrom(this.vmSnapshotData)).addResource(this.fullAssetPathFrom(this.isolateSnapshotData)).addResource(this.fullAssetPathFrom("kernel_blob.bin"));
        this.resourceExtractor.start();
    }
Copy the code

So far only Android has been loading data statically at startup, and related files have been added to the Android directory folder.

Initialize the UI interface, Plug, and event listener callback methods

Initializes the installation path of Flutter files in the main thread. 2.FlutterLoader extracts Flutter files from the asset folder in the APK package to the installation directory

FlutterEngine is a SO library that can only be loaded into the Java execution path to initialize the Dart VIRTUAL machine and provide an environment for Dart to run. In the following articles, some analysis of FlutterEngine initialization process will be made. Application startup in Android is completed. Once the process has been initialized, you can start the Activity, and the start Activity class configured in the manifest inherits FlutterActivity

1. Initialize the Androidwindow property to provide a full screen state for Flutter to use 2. CreateFlutterView provides a mechanism for developers to define their own Flutter SurfaceView. If the createFlutterView user has not customized it, then use the system default SurfaceView 5. FlutterNativeView provides classes for user operations. FlutterNativeView provides Android code and Flutter interoperability 6. Call setContentView to add SurfaceView 7. Add the first frame to the system to avoid a white screen during Flutter initialization

After the above initialization process, the UI has been initialized, but the loading of the Flutter. So file has not been completed

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()); Loading flutter. So the library, through FlutterJNI FlutterMain nativeInit method. 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

FlutterView initialization requires that Flutter related resources be loaded

1. Initialize FlutterNativeView to listen on Flutter. So library DartExecutor: really manage plugin binding and unbinding on FlutterPluginRegistry side, event level processing FlutterJNI: listening Flutter side callback to the Android code logic, so the library level event handling FlutterUiDisplayListener: Flutter initialization is completed will be back to the Android UI change listen 2.

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 = this.mNativeView.getFlutterJNI().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 = newArrayList(); Register system-level plug-in listeningthis.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(new ActivityLifecycleListener() {
            public void onPostResume(a) { 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

SurfaceView event and FlutterEngine event

When the SurfaceView is initialized on the Android end, the return function of the SurfaceView is bound to the method in the JNI layer through the local method in the FlutterJNI class. When the FlutterEngine renders the Flutter, You can pass the relevant Surface to the Android platform for rendering display

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(); }};Copy the code

FlutterMain

Call the Flutter for Android library to initialize the Flutter related files

1.FlutterMain.startInitialization(this);

2. System. LoadLibrary (‘ flutter) load so

Engine invokes the engine/SRC/flutter/shell/platform/android/library_loader. Cc, initialization of the JNI code:

!!!!!!!!! Info “Basically do three things”

* 1. Initialize FlutterMain as a JNI layer callback class * 2. Initialize platform event handling classes * 3. Initialize UI draw Sync signal passingCopy the code

System.loadlibrary calls the procedure

When the Flutter. Jar is initialized, call system. loadLibrary to find the extracted Flutter. So file, call dlopen to open so library, load C++ related resources, call JNI_OnLoad after loading. Once the call to JNI_OnLoad is complete and the so library provides functions to run, the next step is to initialize the code and business logic associated with Flutter

JNI_OnLoad

The corresponding classes in JNI are initialized

  • When the so library is first loaded, the current method is called to do three things:

  • Register Java layer code to JNI layer to facilitate subsequent callbacks to Java layer code

  • engine/src/flutter/shell/platform/android/flutter_main.cc

  • /src/flutter/shell/platform/android/io/flutter/view/FlutterView.java

Initialize the event handling logic for the associated platform View


JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  // Initialize the Java VM.
  fml::jni::InitJavaVM(vm);

  JNIEnv* env = fml::jni::AttachCurrentThread();
  bool result = false;

  // Register FlutterMain.
  result = shell::FlutterMain::Register(env);
  FML_CHECK(result);

  // Register PlatformView
  // Handle platform UI/ event related content, lifecycle management, background execution
  result = shell::PlatformViewAndroid::Register(env);
  FML_CHECK(result);

  // Register VSyncWaiter.
  // Actually handle related UI drawing events
  result = shell::VsyncWaiterAndroid::Register(env);
  FML_CHECK(result);

  return JNI_VERSION_1_4;
}
Copy the code

FlutterMain::Register(env)

Composed in the library_loader library, the engine callback to Java is compiled into code

bool FlutterMain::Register(JNIEnv* env) { static const JNINativeMethod methods[] = { { .name = "nativeInit", .signature = "(Landroid/content/Context; [Ljava/lang/String;Ljava/" "lang/String;Ljava/lang/String;Ljava/lang/String; )V", .fnPtr = reinterpret_cast<void*>(&Init), }, { .name = "nativeRecordStartTimestamp", .signature = "(J)V", .fnPtr = reinterpret_cast<void*>(&RecordStartTimestamp), }, };  jclass clazz = env->FindClass("io/flutter/view/FlutterMain"); if (clazz == nullptr) { return false;  } return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0; }Copy the code

Shell

result = shell::PlatformViewAndroid::Register(env); Corresponding handling/engine/SRC/Java layer flutter/shell/platform/android/platform_view_android. H

class PlatformViewAndroid final : publicPlatformView { <! -- Register not initialized -->public:
    static bool Register(JNIEnv* env);
Copy the code

VsyncWaiterAndroid

VsyncWaiterAndroid::Register(env); Handle the relevant frame rate, synchronization platform layer and dart UI events update, JNI layer callback Java code IO/flutter/view/VsyncWaiter, Android platform layer control classes used to frame rate: android.view.Choreographer

/ / for android with a corresponding flutter in the library: IO. Flutter. The VsyncWaiter
//
bool VsyncWaiterAndroid::Register(JNIEnv* env) {
  static const JNINativeMethod methods[] = {{
      .name = "nativeOnVsync",
      .signature = "(JJJ)V",
      .fnPtr = reinterpret_cast<void*>(&OnNativeVsync),
  }};

  jclass clazz = env->FindClass("io/flutter/view/VsyncWaiter");

  if (clazz == nullptr) {
    return false;
  }

  g_vsync_waiter_class = newfml::jni::ScopedJavaGlobalRef<jclass>(env, clazz); FML_CHECK(! g_vsync_waiter_class->is_null()); g_async_wait_for_vsync_method_ = env->GetStaticMethodID( g_vsync_waiter_class->obj(),"asyncWaitForVsync"."(J)V"); FML_CHECK(g_async_wait_for_vsync_method_ ! =nullptr);

  return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0;
}
Copy the code

flutter_main::nativeInit

After the file extraction in APK is complete, initialize the code in JNI and load the file code related to FLUTTER. io/flutter/view/FlutterMain.class ,nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), appBundlePath, appStoragePath, engineCachesPath); After initialization is complete, ten mile River

NativeInit () in the Java layer can use related commands to transfer related parameters after the extraction of APK files in the Java layer, mainly to transfer related initialization parameters to the JNI layer, which is convenient to load related files to initialize Flutter path information

void FlutterMain::Init(JNIEnv* env, jclass clazz, jobject context, jobjectArray jargs, jstring bundlePath, jstring appStoragePath, jstring engineCachesPath) { std::vector<std::string> args; args.push_back("flutter"); for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) { args.push_back(std::move(arg)); } / / initialization command line parameters auto command_line = FML: : CommandLineFromIterators (args. The begin (), the args. The end ()); auto settings = SettingsFromCommandLine(command_line); / / initialize the resource directory structure Settings. Assets_path = FML: : jni: : JavaStringToString (env, bundlePath); // Restore the callback cache. // TODO(chinmaygarde): The Route all cache file access through FML and remove this / / setter. Blink: / / set the cache directory: DartCallbackCache: : SetCachePath ( fml::jni::JavaStringToString(env, appStoragePath)); / / initializes the Android cache directory FML: : paths: : InitializeAndroidCachesPath (FML: : jni: : JavaStringToString (env, engineCachesPath)); / / the cache data to be loaded from disk blink: : DartCallbackCache: : LoadCacheFromDisk (); // How to run config file path if (! blink::DartVM::IsRunningPrecompiledCode()) { // Check to see if the appropriate kernel files are present and configure // settings accordingly. auto application_kernel_path = fml::paths::JoinPaths({settings.assets_path, "kernel_blob.bin"}); if (fml::IsFile(application_kernel_path)) { settings.application_kernel_asset = application_kernel_path; Task_observer_add = [](intptr_t key, fml::closure callback) { fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); }; / / remove the message queue Settings. Task_observer_remove = [] (intptr_t key) {... FML MessageLoop: GetCurrent () RemoveTaskObserver (key); }; #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG // There are no ownership concerns here as all mappings are owned  by the // embedder and not the engine. auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { return [mapping, size]() { return std::make_unique<fml::NonOwnedMapping>(mapping, size); }; }; settings.dart_library_sources_kernel = make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize); #endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG // Not thread safe. Will be removed when FlutterMain is refactored to no // longer be a singleton. g_flutter_main.reset(new FlutterMain(std::move(settings))); }Copy the code

At this point, the file in Apk has been extracted, the callback method, the callback event has been completed, and the initialization information for the path has been initialized

conclusion

Based on the above analysis, we have initialized the call logic of Flutter and Android. Based on the above analysis, we summarize the general logic of Flutter initialization

1. Start App, call startInitialization method in FlutterMain in Application, complete path configuration, extract flutter code, and load flutter

2. Start initializing the Activity UI and binding Android and Flutter communication, callbacks, and a series of actions in FlutterActivityDelegate

3. Call the nativeInit method in ‘ ‘to initialize the Flutter. So library for DartVM initialization

4. FlutterMain is initialized in the JNI_OnLoad method when the Flutter. So library is loaded as the call entry on Android

5. Initialization PlatformViewAndroid Android terminal and the entrance of the FlutterView communication interaction

6. After the Engine resources are initialized with Flutter. So, call the nativeInit method in flutterLoader.class to initialize them

7. Initialize Flutter using the nativeAttach method in FlutterJNI. So to bind Android and Flutter related event callbacks

8. When the SurfaceView is initialized on the Android end, the return function Callback of the SurfaceView is bound to the method in the JNI layer through the local method in FlutterJNI class. When FlutterEngine renders Flutter, You can pass the relevant Surface to the Android platform for rendering display

By now, Android has started and loaded Flutter resources, initialized FlutterEngine, bound SurfaceView and FlutterEngine objects, and registered system-level plugin functions. In the next article, we will How is the FlutterEngine initialized, after all, to see the Flutter engine code displayed on your phone