一、什么是插件化
插件化解决的问题:
App 体积越来越大,因为功能越来越多,如果App太大,用户都不想下载了
模块之间的耦合度搞,协同开发的成本高了
规避方法数超过 65535 问题,不过可以用 MutiApplication 了
可以用于ABTest,某些用户下载插件A版本,某些下载插件B版本
换肤,多开技术等都可以
选择开源框架的时候,根据自身需求:
如果加载的插件不需要和宿主有任何耦合,也无需和宿主进行通信,那么推荐使用 RePlugin
其他情况推荐 VirtualApk
插件化和组件化的区别?能用插件化的情况下不用组件化,组件化做不到按需下载,代码一起再 App 里面。可以做到各个插件的单独升级和下载。
二、插件化前置知识
插件化我们需要解决的问题:
如何加载插件中的类
如何启动四大组件,因为有生命周期
如何加载插件中的资源
2.1 对于反射的了解
反射的思想:我们一个类,比如 Person 类,它的描述也是可以封装成一个 Class 对象,用于描述其成员变量、构造方法、方法等。
其中的属性,也是个对象 Field ,这个在反射中我们就能用到,所以我们可以构造出 Field 之后,对其进行 set 和 get 来设置和获取值,只不过,如果是静态属性,则get 的时候传入 null 即可,因为它是属于类级别;如果是成员属性,则需要传入这个类的对象,表示你要获取哪个对象的这个属性。
为什么反射耗时:
产生临时对象,导致GC
需要检查可见性,比较耗时
字节码没有优化
会做装箱、拆箱的操作,如 int 会变为 Integer
2.2 类加载器
使用反射的时候,forName 和 loadClass 会有什么区别?
forName 会进行类加载,验证、准备、解析,初始化;
loadClass 只会做类加载
澄清一下网上的错误说法,
其实 PathClassLoader 也是能加载未安装的 APK 的!老师在课程里面用代码验证了。
只是在 8.0 之前,DexClassLoader 构造函数在需要传入一个 odex 存放的位置,就这一个区别。在 8.0 之后基本上没有区别了。
PathClassLoader 和 BootClassLoader 有什么区别?
PathClassLoader 加载应用的类,比如你写的 MainActivity
BootClassLoader 加载 SDK 的类,比如 Activity 类,但是 AppcompactActivity 这种就是
ClassLoader 中 loadClass 方法加载类的步骤:
首先 findLoadedClass 看看类是否已经加载过了,加载过了直接用
其次让parent加载
如果parent加载还没有,就自己 findClass 加载
整体的调用步骤如下图所示:
BootClassLoader shi ClassLoader 的内部,它的 loadClass 只有 2 步, 首先 findLoadedClass , 如果没有直接就自己 find Class 了,因为它上面没有父 ClassLoader 。
为什么Android 要设计这样的双亲委派机制?基于2点:一是避免重复加载,父加载器已经加载的情况下,就不要加载了;二是安全性考虑,防止核心 API 被随意篡改
三、启动插件
通过对 ClassLoader 加载类的源码追踪,我们能看到如下的时序图:
可以知道,最终是通过 DexPathList 去 findClass ,查看里面的源码可以知道:
一个 dex 文件对应一个 Element 对象
但是一个 Apk 可能有多个 dex 文件
所以使用 Element[] dexElements 数组存放app 中所有的 element
所以我们的思想步骤可以分为如下4 步:
获取宿主的 dexElements获取插件的 dexElements
获取插件的 newdexElements
合并 2 个 dexElements 得到新的 newdexElements
newdexElements 赋值给宿主的 dexElements