Flutter Android 端啓動流程淺析

這篇文章主要是分析一下 Flutter Android 端的啓動流程,主要是上層代碼的分析,不涉及底層 c/c++ 等的分析。同時,不一樣 Flutter 版本的 sdk ,代碼也會有所不一樣,可是總體流程和原理不會有太大的不一樣。android

一、FlutterApplication

Android 端 app 的啓動,必定會先初始化 Application,再去加載默認的第一個類 MainActivity。Flutter 項目對應的 Android 端應用程序,application 默認指定爲 FlutterApplication。c++

<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>
複製代碼

1.1 FlutterMain.startInitialization(this);

FlutterApplication 中 onCreate 核心初始化代碼只有一行shell

FlutterMain.startInitialization(this);
複製代碼

FlutterMain 這個類在 flutter.jar 包中。不一樣的平臺對應不一樣的 flutter.jar 包,這個文件在 flutter sdk 的路徑位置爲: flutterSdkPath\flutter\bin\cache\artifacts\engine 。編程

接下來看 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);
            }
        }
    }
複製代碼

主要作了以下幾件事情:緩存

  • startInitialization 這個初始化函數必須在在主線程執行,不然拋出異常。
  • sSettings 這個變量只會初始化一次。
  • initConfig 初始化配置信息。
  • initResources 初始化資源。
  • System.loadLibrary("flutter") 加載 flutter 核心庫 libflutter.so。這個庫也在 flutter.jar 中,編譯 flutter 項目中的時候,這個庫會複製到 apk 中。
  • FlutterJNI.nativeRecordStartTimestamp 主要是調用底層方法記錄初始化時間。

1.2 initConfig

這個方法以下:bash

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");
        }
    }
複製代碼

主要是解析 metadata 初始化默認配置,若是沒有設置則使用默認值。微信

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

1.3 initResources

代碼以下:app

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

複製代碼

主要的做用就兩個:ide

  • 經過 ResourceCleaner 清理緩存文件
  • 經過 sRecourceExtractor 加載指定目錄下的資源文件,經過這些文件將進行 flutter engine 和 Darv Vm 的初始化。

上面就是 Application 的初始化過程。接下來看 MainActivity 的執行。

二、MainActivity

Android 端默認啓動的第一個類就是 MainActivity,而建立的 Flutter 的項目中,MainActivity 這個類都是自動生成的,代碼以下:

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

複製代碼

能夠看到,MainActivity 繼承自 Flutter 本身實現的 FlutterActivity。

2.1 GeneratedPluginRegistrant.registerWith(this)

MainActivity 中的核心代碼只有一行

GeneratedPluginRegistrant.registerWith(this);
複製代碼

GeneratedPluginRegistrant 這個類代碼以下:

/**
 * 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;
  }
}
複製代碼

能夠看到,這個類是自動生成的,而且會 執行 registry.registrarFor(key) 方法。 registry 是一個接口,而且 FlutterActivity 實現了這個接。而 MainActivity 也繼承了 FlutterActivity 。接下來分析 FlutterActivity 。

三、FlutterActivity

public class FlutterActivity extends Activity implements Provider, PluginRegistry, ViewFactory 
複製代碼

能夠看到 FlutterActivity 實現了 Provider、PluginRegistry,ViewFactory 三個接口。

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;
    }
複製代碼

能夠看到,三個引用都指向了同一個對象,也就是 這個 delegate 對象將實現另外三個類聲明的功能。

接着看 FlutterActivity 中的主要方法:

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

    protected void onResume() {
        super.onResume();
        this.eventDelegate.onResume();
    }
   ...
複製代碼

能夠看到,在 FlutterActivity 中的核心方法中,只是簡單的回調了 delegate 中對應的方法,也就是 FlutterActivity 這個類只是一個代理,真正實現功能的必定是 delegate 這個對象對應的類,也就是 FlutterActivityDelegate。

四、 FlutterActivityDelegate

public final class FlutterActivityDelegate implements FlutterActivityEvents, Provider, PluginRegistry 
複製代碼

這個類經過 final 修飾,將不能被繼承,同時確實也實現了三個接口。

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);
            }

        }
    }

複製代碼

onCreate 是這個類初始化以後執行的第一個函數,主要的做用是:

  • 設置沉浸式狀態欄
  • 從 intent 中獲取參數
  • 調用 FlutterMain.ensureInitializationComplete 確保資源初始化完成。
  • 建立 FlutterNativeView 和 FlutterView 對象實例。
  • 設置當前 activity 的視圖佈局爲 flutterView。
  • runBundle 這裏面將經過一些列方法的調用,加載 Dart 代碼。

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);
                }
            }
        }
    }
複製代碼

這裏分析的是 release 模式下的代碼,主要的做用是:

  • 主線程執行
  • sSetting 不能爲空,不然拋出異常。
  • 必須保證 sResourceExtractor 資源提取完成。
  • 將全部的配置參數(如 flutter 核心庫的路徑、flutter APP 項目編譯以後生成的 so 庫的路徑)傳遞給 shellArgs,而且這個 List 數組將會傳遞給底層進行 Dart Vm 的初始等。
  • 經過 FlutterJNI 進行底層初始操做。

4.2 FlutterView 的初始化

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(new ActivityLifecycleListener() {
                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();
        }
    }
    ...
}

複製代碼

能夠看到,FlutterView 繼承了 SurfaceView ,而且實現了 BinaryMessenger,TextureRegistry 兩個接口。

主要的做用:

  • 建立 FlutterNativeView。
  • 建立 CallBack 實例,當執行 Surface 回調函數的時候,會通知底層。
  • 建立平臺通道,並將平臺經過設置信息傳遞給 Dart。

4.3 FlutterNativeView 初始化

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 void onFlutterUiNoLongerDisplayed() {
            }
        };
        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();
    }
    ...
}
複製代碼
  • 初始化對象FlutterPluginRegistry;
  • 初始化對象FlutterJNI;
  • 初始化對象RenderSurfaceImpl,並賦值給mFlutterJNI的成員變量renderSurface;
  • 初始化對象DartExecutor;
  • 設置引擎生命週期回調監聽器;
  • 並執行attach方法
private void attach(FlutterNativeView view, boolean isBackgroundView) {
        this.mFlutterJNI.attachToNative(isBackgroundView);
        this.dartExecutor.onAttachedToJNI();
    }
複製代碼
@UiThread
    public void attachToNative(boolean isBackgroundView) {
        this.ensureRunningOnMainThread();
        this.ensureNotAttachedToNative();
        this.nativePlatformViewId = this.nativeAttach(this, isBackgroundView);
    }
複製代碼

上面兩個函數執行以後,將執行 jni 層方法,進而將執行底層代碼,進行 Flutter 引擎的初始化和啓動操做。

當 Flutter引擎和 Dart Vm 等初始化完成以後,代碼將會執行到 FlutterActivityDelegate 中的 runBundle 方法。

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);
        }

    }
複製代碼

主要是建立 FlutterRunArguments 對象,指定入口函數名稱爲 "main",接着執行 FlutterView 中的 runFromBundle 方法。

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;
            }
        }
    }

複製代碼

在 runFromBundle 中,主要是進一步調用了 jni 層的方法,調用的時候,指定了函數名稱,庫路徑,資源文件等。

@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);
    }
複製代碼

接着將調用底層方法:

private native void nativeRunBundleAndSnapshotFromLibrary(long var1, @NonNull String var3, @Nullable String var4, @Nullable String var5, @NonNull AssetManager var6);
複製代碼

底層方法通過一系列方法的調用,將會執行到 Dart 中的 main 函數,也就是將會運行到咱們寫的 Dart 代碼。

整個過程以下圖所示。

歡迎關注「Flutter 編程開發」微信公衆號 。

相關文章
相關標籤/搜索