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

@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 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 dataDirPath,

@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() {

//…

mExtractTask =

new ExtractTask(mDataDirPath, mResources, mPackageName, mPackageManager, mAssetManager);

mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

return this;

}

//…

private static class ExtractTask extends AsyncTask<Void, Void, Void> {

//…

@Override

protected Void doInBackground(Void… unused) {

final File dataDir = new File(mDataDirPath);

//…

//步骤42、从apk提取释放资源,也就是通过流读取assets下flutter资源释放到对应目录下。

if (!extractAPK(dataDir)) {

return null;

}

//…

return null;

}

//…

}

}

ApplicationInfoLoader 相关分析


ApplicationInfoLoader 的职责犹如其名,就是依据配置或者安装好的 apk 进行各种路径、信息拼接获取。

public final class ApplicationInfoLoader {

//步骤43、一堆可以在AndroidManifest.xml中通过meta-data自定义的name属性。

public static final String PUBLIC_AOT_SHARED_LIBRARY_NAME =

FlutterLoader.class.getName() + ‘.’ + FlutterLoader.AOT_SHARED_LIBRARY_NAME;

public static final String PUBLIC_VM_SNAPSHOT_DATA_KEY =

FlutterLoader.class.getName() + ‘.’ + FlutterLoader.VM_SNAPSHOT_DATA_KEY;

public static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY =

FlutterLoader.class.getName() + ‘.’ + FlutterLoader.ISOLATE_SNAPSHOT_DATA_KEY;

public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =

FlutterLoader.class.getName() + ‘.’ + FlutterLoader.FLUTTER_ASSETS_DIR_KEY;

public static final String NETWORK_POLICY_METADATA_KEY = “io.flutter.network-policy”;

public static final String PUBLIC_AUTOMATICALLY_REGISTER_PLUGINS_METADATA_KEY =

“io.flutter.” + FlutterLoader.AUTOMATICALLY_REGISTER_PLUGINS_KEY;

//…

//步骤44、上面步骤11调用获取一个FlutterApplicationInfo实例。

public static FlutterApplicationInfo load(@NonNull Context applicationContext) {

ApplicationInfo appInfo = getApplicationInfo(applicationContext);

//…

//步骤43、一堆基础路径信息,自定义取不到就用缺省值。

return new FlutterApplicationInfo(

getString(appInfo.metaData, PUBLIC_AOT_SHARED_LIBRARY_NAME),

getString(appInfo.metaData, PUBLIC_VM_SNAPSHOT_DATA_KEY),

getString(appInfo.metaData, PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY),

getString(appInfo.metaData, PUBLIC_FLUTTER_ASSETS_DIR_KEY),

getNetworkPolicy(appInfo, applicationContext),

appInfo.nativeLibraryDir,

clearTextPermitted,

getBoolean(appInfo.metaData, PUBLIC_AUTOMATICALLY_REGISTER_PLUGINS_METADATA_KEY, true));

}

}

VsyncWaiter 相关分析


VsyncWaiter 这个类虽然小,但是来头很大,Flutter VSYNC 的绘制信号核心就来自这货,具体如下:

//一个单例对象实例

public class VsyncWaiter {

//…

@NonNull private final WindowManager windowManager;

private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =

new FlutterJNI.AsyncWaitForVsyncDelegate() {

@Override

public void asyncWaitForVsync(long cookie) {

Choreographer.getInstance()

.postFrameCallback(

new Choreographer.FrameCallback() {

//在下一个frame被渲染时,会执行这个callback。

@Override

public void doFrame(long frameTimeNanos) {

float fps = windowManager.getDefaultDisplay().getRefreshRate();

long refreshPeriodNanos = (long) (1000000000.0 / fps);

//本质调用FlutterJNI的nativeOnVsync方法。

FlutterJNI.nativeOnVsync(

frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);

}

});

}

};

//…

//步骤44、被上面步骤12调用。

public void init() {

//设置异步等待VSYNC,等待FlutterJNI native调用。

FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);

//步骤45、给native层设置刷新帧率fps值。

float fps = windowManager.getDefaultDisplay().getRefreshRate();

FlutterJNI.setRefreshRateFPS(fps);

最后

简历首选内推方式,速度快,效率高啊!然后可以在拉钩,boss,脉脉,大街上看看。简历上写道熟悉什么技术就一定要去熟悉它,不然被问到不会很尴尬!做过什么项目,即使项目体量不大,但也一定要熟悉实现原理!不是你负责的部分,也可以看看同事是怎么实现的,换你来做你会怎么做?做过什么,会什么是广度问题,取决于项目内容。但做过什么,达到怎样一个境界,这是深度问题,和个人学习能力和解决问题的态度有关了。大公司看深度,小公司看广度。大公司面试你会的,小公司面试他们用到的你会不会,也就是岗位匹配度。

面试过程一定要有礼貌!即使你觉得面试官不尊重你,经常打断你的讲解,或者你觉得他不如你,问的问题缺乏专业水平,你也一定要尊重他,谁叫现在是他选择你,等你拿到offer后就是你选择他了。

另外,描述问题一定要慢!不要一下子讲一大堆,慢显得你沉稳、自信,而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide,所以会有个弊端,当我们在面试讲解很容易不知道某个方法怎么读,这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准,有些面试官不耐烦会说“你到底说的是哪个?”这时我们会容易乱了阵脚。正确的发音+沉稳的描述+好听的嗓音决对是一个加分项!

最重要的是心态!心态!心态!重要事情说三遍!面试时间很短,在短时间内对方要摸清你的底子还是比较不现实的,所以,有时也是看眼缘,这还是个看脸的时代。

希望大家都能找到合适自己满意的工作!

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
思路接下来怎么讲更好。现在开发过多依赖ide,所以会有个弊端,当我们在面试讲解很容易不知道某个方法怎么读,这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准,有些面试官不耐烦会说“你到底说的是哪个?”这时我们会容易乱了阵脚。正确的发音+沉稳的描述+好听的嗓音决对是一个加分项!

最重要的是心态!心态!心态!重要事情说三遍!面试时间很短,在短时间内对方要摸清你的底子还是比较不现实的,所以,有时也是看眼缘,这还是个看脸的时代。

希望大家都能找到合适自己满意的工作!

进阶学习视频

[外链图片转存中…(img-gZTkpdyW-1714808912725)]

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-exRnyxGb-1714808912728)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值