其他
面试官:你对插件化有什么了解?
本文作者:九心,原文发布于:九心说。
背景
核心业务功能迭代的时候,千小心,万小心,又是做AB,又是做灰度,最后线上还是出问题了,这个时候只能靠发版解决问题,奈何客户端的发版周期长,并且只有用户升级以后才能解决问题。 有些业务上线以来,用的人不多,占用的包体积还不小,这些功能是否可以动态加载。 开发一个功能,必须提交到应用商店以后,用户才能更新(涉及到监管)。
厂商预装的时候包体积的强制诉求:如果不做插件化,就需要每年预装阶段持续投入人力优化包体积,成本比较高。 对外投放的时候,小包有利于提高用户的转化。
线下推广的时候用户的网络是不稳定的。 对于线上广告投放,用户不可能一直在wifi下,小的包体积可以让用户更快的进入应用内,避免劝退用户。
所以我们可以看到,pdd这一方面做的很出色,仅有25m。
相信没接触过插件化的同学可能会有一些疑问,我们平时打包的时候不是都是一个完整的Apk,为什么可以加载一个单独的Apk?好了,这就是插件化的第一个难点。
1. Java类加载
启动类加载器:由C++语言实现,负责加载Java中的核心类。 扩展类加载器:负责加载Java扩展的核心类之外的类。 应用程序类加载器:负责加载用户类路径上指定的类库。
2. Android类加载
提高安装和升级应用的时间。
如何更多的触发 dex2aot 过程。 对启动热点代码预先aot,比如谷歌的BaselineProfile方案,很多大厂也有自己的方案。
BootClassLoader:用来解决Android系统启动时的核心基础类。 PathClassLoader:Android中默认的类加载器,主要用来加载应用程序自身的类以及系统类库之外的本地代码。 DexClassLoader:加载指定路径下的Apk、Jar包的类。 InMemoryDexClassLoader:Android 8.0 中可以用来加载内存中的Dex文件。
3. 方案实现
单个DexClassLoader方案。 多个DexClassLoader方案。
这是因为,只有在我们宿主包中Manifest文件中注册的四大组件才能够启动,如果没有注册,就会抛出异常,提醒你在Manifest中注册。这也是我们遇到的第二个问题。
1. Activity解决方法
宿主包提前声明组件。 占位组件 + 手动调用组件。 占位组件 + 欺骗系统。
1.1 宿主包提前声明组件
1.2 占位组件 + 手动调用组件
public class Instrumentation {
public static void checkStartActivityResult(int res, Object intent) {
if (!ActivityManager.isStartResultFatalError(res)) {
return;
}
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
...
}
}
}
代码的入侵性比较强,需要统一继承PluginActvity。 对于Activity的启动模式,处理比较繁琐。 改造已有的模块比较繁琐。
1.3 欺骗系统
在途中的第一步,启动PluginActivity跳转的时候,通过Instrument处理的时候,会将PluginActivity的Intent改成占位Activity的Intent,并存入原始Activity的信息。 在图片中的第十三步,等系统验证完成回来创建占位Activity的实例对象,就替换成PluginActivity。
在最终使用之前,我们在插件中的Android资源文件并不能使用,比如说图片、字符串、布局文件等,原因是插件的资源路径并没有被添加。
@Deprecated
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(null);
mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
1 解决方法
合并式:插件和宿主资源可以互相访问,要处理资源冲突。 独立式:无需处理资源冲突,宿主和插件的资源访问比较难处理。
PP为Package Id,代表应用类型:如系统应用、第三方应用、dynamic feature等。 TT为资源类型:如drawabl、layout和string。 EEEE为Entry:代表资源顺序。
修改AAPT生成ResourceId,在编译期间完成修改。 修改resouce.arsc文件。
Qigsaw使用的第一种方案。
《Android插件化技术-原理篇》
《Android插件化技术-原理篇》
《聊聊陈旧的插件化》
《【Android 修炼手册】常用技术篇 -- Android 插件化解析》
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!