Android中的ClassLoader整体概述
Android中的ClassLoader的整体架构继承关系如下:
其中,ClassLoader分别为:
BootClassLoader:与Java中的Bootstrap ClassLoader类似,主要加载Android Framework中的字节码文件。
PathClassLoader:与Java中的App ClassLoader类似,主要加载已经安装到系统中的APK中的字节码文件。
DexClassLoader:与Java中的Customer ClassLoader类似,主要加载自定义路径下的APK或者JAR中的字节码文件(Android中主要是指dex文件,即classes.dex)。
其中,BaseDexClassLoader是PathClassLoader以及DexClassLoader的父类,PathClassLoader以及DexClassLoader的逻辑都在这个父类中实现。
BootClassLoader和PathClassLoader是一个普通的APP所需要的(定制ROM另外说),最基本的ClassLoader,通过下面的代码可以证明:
@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClassLoader cl= this.getClassLoader();
Log.e(TAG,"onCreate: " +cl);while (cl.getParent() != null) {
cl=cl.getParent();
Log.e(TAG,"onCreate: " +cl);
}
}
打印结果只有BootClassLoader和PathClassLoader:
09-06 14:29:01.571 3165-3165/com.nan.loadapkdemo E/MainActivity: onCreate: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.nan.loadapkdemo-1/base.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_dependencies_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_0_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_1_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_2_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_3_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_4_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_5_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_6_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_7_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_8_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.nan.loadapkdemo-1/lib/x86, /system/lib, /vendor/lib]]]09-06 14:29:01.571 3165-3165/com.nan.loadapkdemo E/MainActivity: onCreate: java.lang.BootClassLoader@b173b34
上面就分析完了classes.dex文件是如何通过ClassLoader的初始化装载进来的,下面继续分析一个类是如何通过PathClassLoader或者DexClassLoader进行加载的,这时候就需要看父类BaseDexClassLoader的findClass方法了:
@Overrideprotected Class> findClass(String name) throwsClassNotFoundException {
List suppressedExceptions = new ArrayList();
Class c=pathList.findClass(name, suppressedExceptions);if (c == null) {
ClassNotFoundException cnfe= new ClassNotFoundException("Didn't find class "" + name + "" on path: " +pathList);for(Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}throwcnfe;
}returnc;
}
这里面看到,实际上就是调用了PathList的findClass方法:
public Class findClass(String name, Listsuppressed) {for(Element element : dexElements) {
DexFile dex=element.dexFile;if (dex != null) {
Class clazz=dex.loadClassBinaryName(name, definingContext, suppressed);if (clazz != null) {returnclazz;
}
}
}if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}return null;
}
这个方法就是遍历初始化创建好的内部Element[]数组里面的DexFile对象,最终是通过DexFile的loadClassBinaryName进行加载的:
public Class loadClassBinaryName(String name, ClassLoader loader, Listsuppressed) {return defineClass(name, loader, mCookie, this, suppressed);
}
defineClass的实现如下:
private staticClass defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, Listsuppressed) {
Class result= null;try{
result=defineClassNative(name, loader, cookie, dexFile);
}catch(NoClassDefFoundError e) {if (suppressed != null) {
suppressed.add(e);
}
}catch(ClassNotFoundException e) {if (suppressed != null) {
suppressed.add(e);
}
}returnresult;
}
到最后,defineClassNative是一个native方法:
private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile)
defineClassNative 是通过C/C++实现,这个方法去dex文件中查找指定name的类,并且拼接成Class字节码,返回给Java层。通过native实现是为了提高效率。
整个加载流程一气呵成,如下图所示:
实现动态加载
如下面所示,演示了最简单的动态加载的方案:
public class MainActivity extendsAppCompatActivity {
@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String apkPath= getExternalCacheDir().getAbsolutePath() + "/bundle.apk";
loadApk(apkPath);
}private voidloadApk(String apkPath) {
File optFile= getDir("opt", MODE_PRIVATE);//通过DexClassLoader加载制定的APK文件
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, optFile.getAbsolutePath(), null, getClassLoader());try{//通过反射去使用对象
Class clz = dexClassLoader.loadClass("com.loubinfeng.www.boundle.Printer");if (clz != null) {
Object instance=clz.newInstance();
Method method= clz.getMethod("print");
method.invoke(instance);
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
实现动态加载的难点
实现动态加载难点比较多:
资源的访问问题
四大组件的生命周期管理问题
插件ClassLoader的管理
so库的动态加载等等
参考文章: