Android插件化-开篇
Android插件化从技术上来说就是如何启动未安装的apk(主要是四大组件)里面的类,主要问题涉及如何加载类、如何加载资源、如何管理组件生命周期。
1.类加载 (四大组件)
2.资源的加载
3.如何管理组件生命周期
资源的加载
1 | private AssetManager createAssetManager(String apkPath) { |
Resouces的访问
1 | /** |
如何管理组件生命周期
ClassLoader-类加载
Java - ClassLoader
- Java中默认有三种ClassLoader。分别是:
- BootStrap ClassLoader:启动类加载器,最顶层的加载器。主要负责加载JDK中的核心类。在JVM启动后也随着启动,并构造Ext ClassLoader和App ClassLoader。
- Extension ClassLoader:扩展类加载器,负责加载Java的扩展类库。
- App ClassLoader:系统类加载器,负责加载应用程序的所有jar和class文件。
- 自定义ClassLoader:需要继承自ClassLoader类。
ClassLoader职能
ClassLoader默认使用双亲委托模型来搜索类。
每个ClassLoader都有一个父类的引用。当ClassLoader需要加载某个类时,先判断是否加载过,如果加载过就返回Class对象。否则交给他的父类去加载,继续判断是否加载过。这样 层层判断,就到了最顶层的BootStrap ClassLoader来试图加载。如果连最顶层的Bootstrap ClassLoader都没加载过,那就加载。如果加载失败,就转交给子ClassLoader,层层加载,直到最底层。如果还不能加载的话那就只能抛出异常了。
- 通过这种双亲委托模型,好处是:
- 更高效,父类加载一次就可以避免了子类多次重复加载
- 更安全,避免了外界伪造java核心类。
Android - ClassLoader
android从5.0开始使用art虚拟机,这种虚拟机在程序运行时也需要ClassLoader将类加载到内存中,但是与java不同的是,java虚拟机通过读取class字节码来加载,但是art则是通过dex字节码来加载。
这是一种优化,可以合并多个class文件为一个classes.dex文件。
- android一共有三种类加载器:
- BootClassLoader:父类构造器
- PathClassLoader:一般是加载指定路径/data/app中的apk,也就是安装到手机中的apk。所以一般作为默认的加载器。
- DexClassLoader:从包含classes.dex的jar或者apk中,加载类的加载器,可用于动态加载。
看PathClassLoader和DexClassLoader源码,都是继承自BaseDexClassLoader。我们可以看到PathClassLoader的两个参数都为null,表明只能接受固定的dex文件,而这个文件是只能在安装后出现的。而DexClassLoader中optimizedDirectory,和librarySearchPath都是可以自己定义的,说明我们可以传入一个jar或者apk包,保证解压缩后是一个dex文件就可以操作了。因此,我们通常使用DexClassLoader来进行插件化和热修复。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent); //见下文
}
}
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent); //见下文
//收集dex文件和Native动态库【见小节3.2】
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
}
可以看到,BaseDexClassLoader有一个相当重要的过程就是初始化DexPathList。初始化DexPathList的过程主要是收集dexElements和nativeLibraryPathElements。一个Classloader可以包含多个dex文件,每个dex文件被封装到一个Element对象。这element对象在初始化和热修复逻辑中是相当重要的。当查找某个类时,会遍历dexElements,如果找到就返回,否则继续遍历。所以当多个dex中有相同的类,只会加载前面的dex中的类。下面是这段逻辑的具体实现
1 | public Class findClass(String name, List<Throwable> suppressed) { |
总结
我们先是讲解了Java中类加载的双亲委托机制,然后介绍了Android中的几种ClassLoader,从源码角度介绍了两种ClassLoader加载机制的不同。以后的插件化实践中,我们会经常用到DexClassLoader。