写点什么

Flutter Android 端 FlutterInjector 及依赖流程源码分析

用户头像
Android架构
关注
发布于: 刚刚

if (flutterLoader == null) {


//步骤 6、实例化一个 FlutterLoader。


flutterLoader = new FlutterLoader();


}


// DeferredComponentManager's intended default is null.


}


public FlutterInjector build() {


fillDefaults();


return new FlutterInjector(flutterLoader, deferredComponentManager);


}


}


}


可以看到,FlutterInjector 看起来只是一个全局单例的注入管理类角色,其核心还在于 FlutterInjector 和 DeferredComponentManager,由于我们不是海外市场,所以暂时不分析 DeferredComponentManager 特性,着重关注通用的 FlutterInjector 机制。


FlutterLoader 相关分析




正如其注释说的,这个类的职责是在应用 APK 中查找 Flutter 资源并加载 Flutter 原生库。源码如下:


public class FlutterLoader {


//......


//步骤 7、创建实例,传递 FlutterJNI 实例,下一小节分析 FlutterJNI。


public FlutterLoader() {


this(new FlutterJNI());


}


public FlutterLoader(@NonNull FlutterJNI flutterJNI) {


this.flutterJNI = flutterJNI;


}


//......


//步骤 8、初始化结果结构体。


private static class InitResult {


final String appStoragePath;


final String engineCachesPath;


final String dataDirPath;


//......


}


//步骤 9、FlutterEngine 实例化中调用,重要开始入口。


public void startInitialization(@NonNull Context applicationContext) {


startInitialization(applicationContext, new Settings());


}


//步骤 10、加载 Flutter Engine native so 库,定位 Dart resources 在 apk 中的路径。


//必须在主线程调用此方法。


public void startInitialization(@NonNull Context applicationContext, @NonNull Settings settings) {


//......


if (Looper.myLooper() != Looper.getMainLooper()) {


throw new IllegalStateException("startInitialization must be called on the main thread");


}


final Context appContext = applicationContext.getApplicationContext();


this.settings = settings;


initStartTimestampMillis = SystemClock.uptimeMillis();


//步骤 11、通过 ApplicationInfoLoader 的 load 获取 FlutterApplicationInfo 信息。


//里面主要都是 flutterAssetsDir、nativeLibraryDir、aotSharedLibraryName 等信息,下一小节专门分析 FlutterApplicationInfo。


flutterApplicationInfo = ApplicationInfoLoader.load(appContext);


//步骤 12、平台 VSYNC 信号相关初始化设置,下面单独小节分析。


VsyncWaiter.getInstance((WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE))


.init();


//步骤 13、由于需要访问磁盘,异步调用执行初始化相关东西。


Callable<InitResult> initTask =


new Callable<InitResult>() {


@Override


public InitResult call() {


//步骤 14、异步调用 initResources 得到 ResourceExtractor。


ResourceExtractor resourceExtractor = initResources(appContext);


//步骤 15、通过 flutterJNI 调用加载框架 so,即 libflutter.so


flutterJNI.loadLibrary();


//步骤 16、异步之上再异步先尽可能快获取默认的 font manager。


Executors.newSingleThreadExecutor()


.execute(


new Runnable() {


@Override


public void run() {


flutterJNI.prefetchDefaultFontManager();


}


});


//步骤 17、等待 ResourceExtractor 异步任务结束。


if (resourceExtractor != null) {


resourceExtractor.waitForCompletion();


}


//步骤 18、返回异步执行的结果结构。


return new InitResult(


PathUtils.getFilesDir(appContext),


PathUtils.getCacheDirectory(appContext),


PathUtils.getDataDirectory(appContext));


}


};


//步骤 19、通过线程池提交 Callable 并返回一个 Future 实例。


initResultFuture = Executors.newSingleThreadExecutor().submit(initTask);


}


//......


//步骤 20、提取 apk 中 assets 文件为未压缩的在磁盘中,上面步骤 14 调用。


private ResourceExtractor initResources(@NonNull Context applicationContext) {


ResourceExtractor resourceExtractor = null;


//步骤 21、如果是 debug 或 jit 模式 resourceExtractor 才不为空。


if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {


//步骤 22、路径为 context.getDir("flutter"),也就是私有沙盒下的 flutter 目录。


final String dataDirPath = PathUtils.getDataDirectory(applicationContext);


//步骤 23、获取 apk 的应用包名。


final String packageName = applicationContext.getPackageName();


final PackageManager packageManager = applicationContext.getPackageManager();


final AssetManager assetManager = applicationContext.getResources().getAssets();


//步骤 24、拿着一堆 apk 信息和目录实例化一个 ResourceExtractor 供后续调用。


resourceExtractor =


new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager);


//步骤 25、debug 及 jit 模式填充路径。flutterApplicationInfo 来自上面步骤 11 实例化构建。


//默认资源路径:flutter_assets/vm_snapshot_data


//默认资源路径:flutter_assets/isolate_snapshot_data


//默认资源路径:flutter_assets/kernel_blob.bin


resourceExtractor


.addResource(fullAssetPathFrom(flutterApplicationInfo.vmSnapshotData))


.addResource(fullAssetPathFrom(flutterApplicationInfo.isolateSnapshotData))


.addResource(fullAssetPathFrom(DEFAULT_KERNEL_BLOB));


//步骤 26、开始执行 resourceExtractor。


resourceExtractor.start();


}


return resourceExtractor;


}


//......


}


到此初始化就异步开始了,我们需要阻塞等待他的执行结束,如下:


public class FlutterLoader {


//......


//步骤 27、阻塞直到上面步骤 10 的 startInitialization 方法执行完毕。


//一般紧挨在 startInitialization 方法后调用。


public void ensureInitializationComplete(


@NonNull Context applicationContext, @Nullable String[] args) {


//......


try {


//步骤 28、阻塞等待上面步骤 19 的线程池 Callable initTask 执行完毕。


InitResult result = initResultFuture.get();


//步骤 29、构建一堆参数供初始化使用。


List<String> shellArgs = new ArrayList<>();


shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");


//步 30、flutterApplicationInfo 在上面步骤 11 构建。


//路径为:[ApplicationInfo.nativeLibraryDir]/libflutter.so


shellArgs.add(


"--icu-native-lib-path="


  • flutterApplicationInfo.nativeLibraryDir

  • File.separator

  • DEFAULT_LIBRARY);


if (args != null) {


//步 31、把启动 FlutterActivity 或 FlutterFragment 时 intent 中传递的 dartVmArgs 参数加入列表。


//譬如:trace-startup=true 等


Collections.addAll(shellArgs, args);


}


String kernelPath = null;


//步 32、debug、jit 模式设置一些参数路径啥的。


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 {


//步骤 33、release 模式重点!!!!


//前几天还有网友微信我说想让分析 flutter appso 热更新,这就是核心啊,就这一句话。


//--aot-shared-library-name=缺省 libapp.so,可以通过清单文件 meta-data 配置 so 的名称为自定义值,配置 name 为 io.flutter.embedding.engine.loader.FlutterLoader.aot-shared-library-name。


shellArgs.add(


"--" + AOT_SHARED_LIBRARY_NAME + "=" + flutterApplicationInfo.aotSharedLibraryName);


//步骤 34、--aot-shared-library-name 再配置一个 apk 安装后包路径下 so 的全路径地址。


shellArgs.add(


"--"


  • AOT_SHARED_LIBRARY_NAME

  • "="

  • flutterApplicationInfo.nativeLibraryDir

  • File.separator

  • flutterApplicationInfo.aotSharedLibraryName);


}


//步骤 35、一堆同理的参数路径配置啥的。


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


}


//......


//步骤 36、进行框架真正的 native 层代码初始化,传入我们准备的一切。


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


}


}


//......


//步骤 37、dart 使用资源 resources 的路径。


//本质可以通过 meta-data 配置 name 为 io.flutter.embedding.engine.loader.FlutterLoader.flutter-assets-dir。


//缺省为 flutter_assets。


@NonNull


public String findAppBundlePath() {


return flutterApplicationInfo.flutterAssetsDir;


}


//......


//步骤 38、flutter plugin 是否自动配置注册,默认是 true,同样可以通过 meta-data 配置变更。


@NonNull


public boolean automaticallyRegisterPlugins() {


return flutterApplicationInfo.automaticallyRegisterPlugins;


}


//......


}


可以看到,FlutterLoader 在调用 ensureInitializationComplete 方法时会将编译进 apk 中的 Flutter 相关libapp.so、assets 下面资源路径等各种安卓平台路径进行拼接传递给 flutterJNI 的 init 初始化。也就是说,Flutter Engine 拿到的关于 Flutter App 的各种原始资源路径都来自安卓平台解析传递,对于 Engine 来说就是一个 File path 的概念。这也就给我们进行 File path 重定向提供了思路,带来的国内团队骚操作就是衍生出了 Flutter app.so 热更新能力。


ResourceExtractor 相关分析




ResourceExtractor 类主要通过线程池异步解析安装好的 apk 文件,释放 assets 路径下 Flutter 相关的资源,为 Flutter Engine 使用提供便利路径。


class ResourceExtractor {


//......


//步骤 39、依据标准安卓系统获取支持的 abi 列表


private static final String[] SUPPORTED_ABIS = getSupportedAbis();


//步骤 40、构造方法,在上面步骤 24 中被调用。


ResourceExtractor(


@NonNull String dataDirP


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ath,


@NonNull String packageName,


@NonNull PackageManager packageManager,


@NonNull AssetManager assetManager) {


mDataDirPath = dataDirPath;


mPackageName = packageName;


mPackageManager = packageManager;


mAssetManager = assetManager;


mResources = new HashSet<>();


}


//......


//步骤 41、新建一个 ExtractTask 并执行,本质是一个 AsyncTask。


ResourceExtractor start() {


//......

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter Android 端 FlutterInjector 及依赖流程源码分析