Flutter Android端启动加载流程梳理

1. FlutterApplication

Android应用的启动会先初始化Application,再加载默认的MainActivity。Flutter项目中,Android端的Application默认指定为FlutterApplication或自定义的Application。

1.1 FlutterInjector.instance().flutterLoader().startInitialization(this)

ini 复制代码
public void startInitialization(@NonNull Context applicationContext, @NonNull Settings settings) {
    if (this.settings != null) {
        return;
    }
    if (Looper.myLooper() != Looper.getMainLooper()) {
        throw new IllegalStateException("startInitialization must be called on the main thread");
    }

    TraceSection.begin("FlutterLoader#startInitialization");
    try {
        final Context appContext = applicationContext.getApplicationContext();
        this.settings = settings;
        initStartTimestampMillis = SystemClock.uptimeMillis();
        flutterApplicationInfo = ApplicationInfoLoader.load(appContext);

        VsyncWaiter waiter;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            DisplayManager dm = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
            waiter = VsyncWaiter.getInstance(dm, flutterJNI);
        } else {
            float fps = ((WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay()
                    .getRefreshRate();
            waiter = VsyncWaiter.getInstance(fps, flutterJNI);
        }
        waiter.init();

        Callable<InitResult> initTask = new Callable<InitResult>() {
            @Override
            public InitResult call() {
                TraceSection.begin("FlutterLoader initTask");
                try {
                    ResourceExtractor resourceExtractor = initResources(appContext);
                    flutterJNI.loadLibrary();
                    flutterJNI.updateRefreshRate();
                    executorService.execute(() -> flutterJNI.prefetchDefaultFontManager());
                    if (resourceExtractor != null) {
                        resourceExtractor.waitForCompletion();
                    }
                    return new InitResult(
                        PathUtils.getFilesDir(appContext),
                        PathUtils.getCacheDirectory(appContext),
                        PathUtils.getDataDirectory(appContext)
                    );
                } finally {
                    TraceSection.end();
                }
            }
        };
        initResultFuture = executorService.submit(initTask);
    } finally {
        TraceSection.end();
    }
}

上述代码是Flutter引擎初始化的核心部分,主要完成以下工作:

  • 获取应用上下文和配置信息
  • 初始化VsyncWaiter用于帧同步
  • 在后台线程中执行资源提取、库加载等耗时操作

关键步骤说明:

  • ResourceExtractor resourceExtractor = initResources(appContext);
    从APK中提取Flutter所需的资源文件(如Dart代码、字体、图片等),供引擎使用。
  • flutterJNI.loadLibrary();
    加载Flutter的C++原生库(libflutter.so),包含Skia渲染引擎、Dart VM等核心组件。
  • flutterJNI.updateRefreshRate();
    将屏幕刷新率信息传递给原生层,确保Flutter渲染帧率与设备刷新率匹配。

2. MainActivity

Android端默认启动的第一个Activity是MainActivity,Flutter项目中自动生成的代码如下:

less 复制代码
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    GeneratedPluginRegistrant.registerWith(flutterEngine);
}

核心代码:
GeneratedPluginRegistrant.registerWith(this);

该方法是自动生成的,用于注册所有支持Android平台的Flutter插件。


3. FlutterActivity

FlutterActivity本身是一个代理类,实际功能由FlutterActivityDelegate实现。所有与Flutter核心操作相关的Android Activity方法都应转发给FlutterActivityDelegate


4. FlutterActivityDelegate

该类为final类,不可继承,实现了三个接口。

onCreate方法

  • 设置沉浸式状态栏
  • 从Intent中获取参数
  • 调用FlutterMain.ensureInitializationComplete确保资源初始化完成
  • 创建FlutterNativeViewFlutterView实例
  • 设置Activity的布局为flutterView
  • 调用runBundle加载Dart代码

4.1 FlutterMain.ensureInitializationComplete

java 复制代码
· public void ensureInitializationComplete(  
    @NonNull Context applicationContext, @Nullable String[] args) {  
  if (initialized) {  
    return;  
  }  
  if (Looper.myLooper() != Looper.getMainLooper()) {  
    throw new IllegalStateException(  
        "ensureInitializationComplete must be called on the main thread");  
  }  
  if (settings == null) {  
    throw new IllegalStateException(  
        "ensureInitializationComplete must be called after startInitialization");  
  }  
  
  TraceSection.begin("FlutterLoader#ensureInitializationComplete");  
  try {  
    InitResult result = initResultFuture.get();  
  
    List<String> shellArgs = new ArrayList<>();  
    shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");  
  
    shellArgs.add(  
        "--icu-native-lib-path="  
            + flutterApplicationInfo.nativeLibraryDir  
            + File.separator  
            + DEFAULT_LIBRARY);  
    if (args != null) {  
      Collections.addAll(shellArgs, args);  
    }  
  
    String kernelPath = null;  
    if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {  
      String snapshotAssetPath =  
          result.dataDirPath + File.separator + flutterApplicationInfo.flutterAssetsDir;  
      kernelPath = snapshotAssetPath + File.separator + DEFAULT_KERNEL_BLOB;  
      shellArgs.add("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + snapshotAssetPath);  
      shellArgs.add("--" + VM_SNAPSHOT_DATA_KEY + "=" + flutterApplicationInfo.vmSnapshotData);  
      shellArgs.add(  
          "--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + flutterApplicationInfo.isolateSnapshotData);  
    } else {  
      shellArgs.add(  
          "--" + AOT_SHARED_LIBRARY_NAME + "=" + flutterApplicationInfo.aotSharedLibraryName);  
  
      // Most devices can load the AOT shared library based on the library name  
      // with no directory path.  Provide a fully qualified path to the library  
      // as a workaround for devices where that fails.  
      shellArgs.add(  
          "--"  
              + AOT_SHARED_LIBRARY_NAME  
              + "="  
              + flutterApplicationInfo.nativeLibraryDir  
              + File.separator  
              + flutterApplicationInfo.aotSharedLibraryName);  
  
      // In profile mode, provide a separate library containing a snapshot for  
      // launching the Dart VM service isolate.  
      if (BuildConfig.PROFILE) {  
        shellArgs.add(  
            "--" + AOT_VMSERVICE_SHARED_LIBRARY_NAME + "=" + VMSERVICE_SNAPSHOT_LIBRARY);  
      }  
    }  
  
    shellArgs.add("--cache-dir-path=" + result.engineCachesPath);  
    if (flutterApplicationInfo.domainNetworkPolicy != null) {  
      shellArgs.add("--domain-network-policy=" + flutterApplicationInfo.domainNetworkPolicy);  
    }  
    if (settings.getLogTag() != null) {  
      shellArgs.add("--log-tag=" + settings.getLogTag());  
    }  
  
    ApplicationInfo applicationInfo =  
        applicationContext  
            .getPackageManager()  
            .getApplicationInfo(  
                applicationContext.getPackageName(), PackageManager.GET_META_DATA);  
    Bundle metaData = applicationInfo.metaData;  
    int oldGenHeapSizeMegaBytes =  
        metaData != null ? metaData.getInt(OLD_GEN_HEAP_SIZE_META_DATA_KEY) : 0;  
    if (oldGenHeapSizeMegaBytes == 0) {  
      // default to half of total memory.  
      ActivityManager activityManager =  
          (ActivityManager) applicationContext.getSystemService(Context.ACTIVITY_SERVICE);  
      ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();  
      activityManager.getMemoryInfo(memInfo);  
      oldGenHeapSizeMegaBytes = (int) (memInfo.totalMem / 1e6 / 2);  
    }  
    shellArgs.add("--old-gen-heap-size=" + oldGenHeapSizeMegaBytes);  
  
    DisplayMetrics displayMetrics = applicationContext.getResources().getDisplayMetrics();  
    int screenWidth = displayMetrics.widthPixels;  
    int screenHeight = displayMetrics.heightPixels;  
    // This is the formula Android uses.  
    // https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45  
    int resourceCacheMaxBytesThreshold = screenWidth * screenHeight * 12 * 4;  
    shellArgs.add("--resource-cache-max-bytes-threshold=" + resourceCacheMaxBytesThreshold);  
  
    shellArgs.add("--prefetched-default-font-manager");  
  
    boolean enableSkParagraph =  
        metaData == null || metaData.getBoolean(ENABLE_SKPARAGRAPH_META_DATA_KEY, true);  
    shellArgs.add("--enable-skparagraph=" + enableSkParagraph);  
  
    if (metaData != null && metaData.getBoolean(ENABLE_IMPELLER_META_DATA_KEY, false)) {  
      shellArgs.add("--enable-impeller");  
    }  
  
    final String leakVM = isLeakVM(metaData) ? "true" : "false";  
    shellArgs.add("--leak-vm=" + leakVM);  
  
    long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;  
  
    flutterJNI.init(  
        applicationContext,  
        shellArgs.toArray(new String[0]),  
        kernelPath,  
        result.appStoragePath,  
        result.engineCachesPath,  
        initTimeMillis);  
  
    initialized = true;  
  } catch (Exception e) {  
    Log.e(TAG, "Flutter initialization failed.", e);  
    throw new RuntimeException(e);  
  } finally {  
    TraceSection.end();  
  }  
}

该方法是Debug/Release模式下的关键初始化步骤

  • 确保在主线程执行
  • 检查资源是否提取完成
  • 构建并传递配置参数给底层
  • 通过FlutterJNI完成Dart VM的初始化

4.2 FlutterView创建

FlutterView继承SurfaceView,实现BinaryMessengerTextureRegistry接口,主要功能包括:

  • 创建FlutterNativeView
  • 设置Surface回调
  • 创建平台通道并传递给Dart端

4.3 FlutterNativeView初始化

  • 初始化FlutterPluginRegistryFlutterJNIDartExecutor等对象
  • 设置引擎生命周期监听器
  • 执行attach方法,将FlutterJNI附加到原生引擎
scss 复制代码
private void attach(FlutterNativeView view) {
    mFlutterJNI.attachToNative();
    dartExecutor.onAttachedToJNI();
}

4.4 runBundle

ini 复制代码
private void runBundle(String appBundlePath) {
    if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
        FlutterRunArguments args = new FlutterRunArguments();
        args.bundlePath = appBundlePath;
        args.entrypoint = "main";
        flutterView.runFromBundle(args);
    }
}

java

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

流程说明:

  1. 创建FlutterRunArguments对象,指定入口函数为"main"
  2. 调用FlutterView.runFromBundle,进一步调用JNI层方法
  3. 底层方法最终执行到Dart的main函数,启动Flutter应用

总结

Flutter Android端的启动流程主要包括:

  1. Application初始化:加载资源、库文件,配置渲染环境
  2. Activity启动 :通过FlutterActivityFlutterActivityDelegate代理处理
  3. 引擎初始化 :通过FlutterJNI与底层交互,初始化Dart VM
  4. Dart代码执行 :通过runBundle最终调用Dart的main函数

整个过程涉及多线程协作、资源提取、JNI调用等复杂机制,但Flutter框架已封装大部分细节,开发者只需关注业务逻辑即可。

相关推荐
前端老鹰2 分钟前
CSS outline-offset:让焦点样式不再 “紧贴” 元素的实用属性
前端·css
掘金安东尼10 分钟前
Rspack 推出 Rslint:一个用 Go 编写的 TypeScript-First Linter
前端·javascript·github
一枚前端小能手10 分钟前
正则~~~来看这里
前端·正则表达式
你听得到1114 分钟前
弹窗库1.1.0版本发布!不止于统一,更是全面的体验升级!
android·前端·flutter
RaidenLiu15 分钟前
Riverpod 3 :掌握异步任务处理与 AsyncNotifier
前端·flutter
前端付豪19 分钟前
🔥Vue3 Composition API 核心特性深度解析:为什么说它是前端的“终极武器”?
前端·vue.js
skeletron201129 分钟前
【基础】React工程配置(基于Vite配置)
前端
怪可爱的地球人30 分钟前
前端
蓝胖子的小叮当38 分钟前
JavaScript基础(十四)字符串方法总结
前端·javascript
跟橙姐学代码1 小时前
Python 函数实战手册:学会这招,代码能省一半!
前端·python·ipython