Flutter 系列文章连载~ 《Flutter Android 工程结构及应用层编译源码深入分析》 《Flutter 命令本质之 Flutter tools 机制源码深入分析》 《Flutter 的 runApp 与三棵树诞生流程源码分析》 《Flutter Android 端 Activity/Fragment 流程源码分析》 《Flutter Android 端 FlutterInjector 及依赖流程源码分析》
背景
经过前面的系列分析,这一篇会比较简单。之所以独立一个篇幅是因为本篇内容对于这个系列来说处于承上启下的作用,即是对前面的一个补充,也是对后面的一个引导,包括后续对于 Flutter Android 平台热更新机制的实现也需要依赖本篇内容。
源码分析
这部分我们主要分析的是 FlutterInjector、FlutterLoader、ResourceExtractor、ApplicationInfoLoader、VsyncWaiter,他们的职责其实都很明确且单一,所以这几个知识点是为了这个系列接下来分析 Flutter VSYNC、FlutterView 等篇章做好准备。
FlutterInjector 相关分析
FlutterInjector 是 Android 平台与 Flutter Engine 真正桥梁的管理灵魂控制类,就像他名字中的 Injector 一词一样。
public final class FlutterInjector {
public static FlutterInjector instance() {
accessed = true;
if (instance == null) {
instance = new Builder().build();
}
return instance;
}
public static void setInstance(@NonNull FlutterInjector injector) {
if (accessed) {
throw new IllegalStateException(
"Cannot change the FlutterInjector instance once it's been "
+ "read. If you're trying to dependency inject, be sure to do so at the beginning of "
+ "the program");
}
instance = injector;
}
@NonNull
public FlutterLoader flutterLoader() {
return flutterLoader;
}
public DeferredComponentManager deferredComponentManager() {
return deferredComponentManager;
}
public static final class Builder {
private FlutterLoader flutterLoader;
private DeferredComponentManager deferredComponentManager;
private void fillDefaults() {
if (flutterLoader == null) {
flutterLoader = new FlutterLoader();
}
}
public FlutterInjector build() {
fillDefaults();
return new FlutterInjector(flutterLoader, deferredComponentManager);
}
}
}
可以看到,FlutterInjector 看起来只是一个全局单例的注入管理类角色,其核心还在于 FlutterInjector 和 DeferredComponentManager,由于我们不是海外市场,所以暂时不分析 DeferredComponentManager 特性,着重关注通用的 FlutterInjector 机制。
FlutterLoader 相关分析
正如其注释说的,这个类的职责是在应用 APK 中查找 Flutter 资源并加载 Flutter 原生库。源码如下:
public class FlutterLoader {
public FlutterLoader() {
this(new FlutterJNI());
}
public FlutterLoader(@NonNull FlutterJNI flutterJNI) {
this.flutterJNI = flutterJNI;
}
private static class InitResult {
final String appStoragePath;
final String engineCachesPath;
final String dataDirPath;
}
public void startInitialization(@NonNull Context applicationContext) {
startInitialization(applicationContext, new Settings());
}
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();
flutterApplicationInfo = ApplicationInfoLoader.load(appContext);
VsyncWaiter.getInstance((WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE))
.init();
Callable<InitResult> initTask =
new Callable<InitResult>() {
@Override
public InitResult call() {
ResourceExtractor resourceExtractor = initResources(appContext);
flutterJNI.loadLibrary();
Executors.newSingleThreadExecutor()
.execute(
new Runnable() {
@Override
public void run() {
flutterJNI.prefetchDefaultFontManager();
}
});
if (resourceExtractor != null) {
resourceExtractor.waitForCompletion();
}
return new InitResult(
PathUtils.getFilesDir(appContext),
PathUtils.getCacheDirectory(appContext),
PathUtils.getDataDirectory(appContext));
}
};
initResultFuture = Executors.newSingleThreadExecutor().submit(initTask);
}
private ResourceExtractor initResources(@NonNull Context applicationContext) {
ResourceExtractor resourceExtractor = null;
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
final String dataDirPath = PathUtils.getDataDirectory(applicationContext);
final String packageName = applicationContext.getPackageName();
final PackageManager packageManager = applicationContext.getPackageManager();
final AssetManager assetManager = applicationContext.getResources().getAssets();
resourceExtractor =
new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager);
resourceExtractor
.addResource(fullAssetPathFrom(flutterApplicationInfo.vmSnapshotData))
.addResource(fullAssetPathFrom(flutterApplicationInfo.isolateSnapshotData))
.addResource(fullAssetPathFrom(DEFAULT_KERNEL_BLOB));
resourceExtractor.start();
}
return resourceExtractor;
}
}
到此初始化就异步开始了,我们需要阻塞等待他的执行结束,如下:
public class FlutterLoader {
public void ensureInitializationComplete(
@NonNull Context applicationContext, @Nullable String[] args) {
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);
shellArgs.add(
"--"
+ AOT_SHARED_LIBRARY_NAME
+ "="
+ flutterApplicationInfo.nativeLibraryDir
+ File.separator
+ flutterApplicationInfo.aotSharedLibraryName);
}
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());
}
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);
}
}
@NonNull
public String findAppBundlePath() {
return flutterApplicationInfo.flutterAssetsDir;
}
@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 {
private static final String[] SUPPORTED_ABIS = getSupportedAbis();
ResourceExtractor(
@NonNull String dataDirPath,
@NonNull String packageName,
@NonNull PackageManager packageManager,
@NonNull AssetManager assetManager) {
mDataDirPath = dataDirPath;
mPackageName = packageName;
mPackageManager = packageManager;
mAssetManager = assetManager;
mResources = new HashSet<>();
}
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);
if (!extractAPK(dataDir)) {
return null;
}
return null;
}
}
}
ApplicationInfoLoader 相关分析
ApplicationInfoLoader 的职责犹如其名,就是依据配置或者安装好的 apk 进行各种路径、信息拼接获取。
public final class ApplicationInfoLoader {
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;
public static FlutterApplicationInfo load(@NonNull Context applicationContext) {
ApplicationInfo appInfo = getApplicationInfo(applicationContext);
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() {
@Override
public void doFrame(long frameTimeNanos) {
float fps = windowManager.getDefaultDisplay().getRefreshRate();
long refreshPeriodNanos = (long) (1000000000.0 / fps);
FlutterJNI.nativeOnVsync(
frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
}
});
}
};
public void init() {
FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);
float fps = windowManager.getDefaultDisplay().getRefreshRate();
FlutterJNI.setRefreshRateFPS(fps);
}
}
可以看到,Flutter 绘制 VSYNC 信号的本质也是类似安卓的Choreographer.getInstance().postFrameCallback 操作,本质没啥特殊的,关于其与 Flutter Engine 的关联我们后面会有一篇专门的 Flutter VSYNC 信号触发机制分析文章,到时见详细解释其关联机制,这里先知道有这么个东西和这个时机即可。
|