//步骤13、由于需要访问磁盘,异步调用执行初始化相关东西。
Callable initTask =
new Callable() {
@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 类主要通过线程池异步解析安装好的 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 的职责犹如其名,就是依据配置或者安装好的 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));
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
)】**
[外链图片转存中…(img-xcH4dvrM-1711934418668)]
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
[外链图片转存中…(img-sWjAUJfO-1711934418668)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。