如果文章有问题,请及时指出
一 DexClassLoader 和 PathClassLoader的区别
android 28源码 [https://www.androidos.net.cn/sourcecode](https://www.androidos.net.cn/sourcecode)
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
android28:DexClassLoader和PatchCloasLoader都是继承BaseDexClassLoader,只是构建方法中参数区别,使用无区别。
android 26源码
public class DexClassLoader extends BaseDexClassLoader {
public 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 PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
android 26源码:DexClassLoader可以可以设置optimizedDirectory参数。PathClassLoader不可以,只为空。android26及以后,optimizedDirectory就不用了,没有传给DexPathList。
二 Android 类加载器
android中总共3中类加载器:
BootClassLoader:加载sdk下类
PathClassLoader:加载apk中类,依赖库, 加载自定义apk中类
DexClassLoader:加载自定义apk中类
1 使用的类加载器
public void click(View view) {
ClassLoader classLoader = getClassLoader();
while(classLoader != null) {
Log.i(TAG, "click: " + classLoader);
classLoader = classLoader.getParent();
}
Log.i(TAG, "click: Activity.class " + Activity.class.getClassLoader());
Log.i(TAG, "click: String.class " + String.class.getClassLoader());
Log.i(TAG, "click: Context.class " + Context.class.getClassLoader());
Log.i(TAG, "click: View.class " + View.class.getClassLoader());
Log.i(TAG, "click: ActivityCompat.class " + ActivityCompat.class.getClassLoader());
Log.i(TAG, "click: AppCompatActivity.class " + AppCompatActivity.class.getClassLoader());
Log.i(TAG, "click: MainActivity.class" + this.getClassLoader());
}
日志如下:
2020-08-13 11:38:03.740 30845-30845/com.example.myplugintest I/liuwei13: click: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.myplugintest-9D9w8JkKF_M-NbI5eVEq7g==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.myplugintest-9D9w8JkKF_M-NbI5eVEq7g==/lib/arm64, /system/lib64, /system/product/lib64]]]
2020-08-13 11:38:03.740 30845-30845/com.example.myplugintest I/liuwei13: click: java.lang.BootClassLoader@4d14932
2020-08-13 11:38:03.740 30845-30845/com.example.myplugintest I/liuwei13: click: Activity.class java.lang.BootClassLoader@4d14932
2020-08-13 11:38:03.740 30845-30845/com.example.myplugintest I/liuwei13: click: String.class java.lang.BootClassLoader@4d14932
2020-08-13 11:38:03.740 30845-30845/com.example.myplugintest I/liuwei13: click: Context.class java.lang.BootClassLoader@4d14932
2020-08-13 11:38:03.740 30845-30845/com.example.myplugintest I/liuwei13: click: View.class java.lang.BootClassLoader@4d14932
2020-08-13 11:38:03.741 30845-30845/com.example.myplugintest I/liuwei13: click: ActivityCompat.class dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.myplugintest-9D9w8JkKF_M-NbI5eVEq7g==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.myplugintest-9D9w8JkKF_M-NbI5eVEq7g==/lib/arm64, /system/lib64, /system/product/lib64]]]
2020-08-13 11:38:03.741 30845-30845/com.example.myplugintest I/liuwei13: click: AppCompatActivity.class dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.myplugintest-9D9w8JkKF_M-NbI5eVEq7g==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.myplugintest-9D9w8JkKF_M-NbI5eVEq7g==/lib/arm64, /system/lib64, /system/product/lib64]]]
2020-08-14 16:21:33.557 9161-9161/com.example.myplugintest I/liuwei13: click: MainActivity.classdalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.myplugintest-2NFvCRXDe1LWgjfRoGlcPg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.myplugintest-2NFvCRXDe1LWgjfRoGlcPg==/lib/arm64, /system/lib64, /system/product/lib64]]]
PathClassLoader的parent是BootClassLoader。
SDK中类由BootClassLoader加载:
Activity.class、Context.class、View.class、String.class 等类加载器 java.lang.BootClassLoader@4d14932
依赖库中类由PathClassLoader加载:
AppCompatActivity.class、ActivityCompat.class类加载器 dalvik.system.PathClassLoader
2 android类加载过程
双亲委派机制:
PathClassLoader的父加载器为BootClassLoader。我们使用DexClassLoader加载插件,把patchClassLoader设置成parent。
这样就形成了一个链表。大致流程如下:
版本名称: Pie API Level: 28
public abstract class ClassLoader {
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
// 1 先查找是否已经加载了
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
// 2 调用父加载器的loadClass
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
// 3 自己加载类
c = findClass(name);
}
}
return c;
}
}
BootClassLoader重写了loadClass方法。
class BootClassLoader extends ClassLoader {
.......
@Override
protected Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
// 1 查找已经加载的类
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
// 2 自己加载类
clazz = findClass(className);
}
return clazz;
}
......
}
类加载器类图:
三 加载插件中类
插件加载思路:
1 自己定义DexClassloader加载apk,获得dexElements 。(这里也可以用PathClassLoader加载apk)
2 获得宿主的dexElementst,与1 中dexElements合并成一个数组。
3 把合并的数组设置给宿主。
**加载插件类的源码:注意sdcard权限**
private void installPlugin() {
try {
// 1 获取dalvik.system.DexPathList.dexElements
Class dexPathListClass = Class.forName("dalvik.system.DexPathList");
Field dexElementsField = dexPathListClass.getDeclaredField("dexElements");
dexElementsField.setAccessible(true);
// 2 获取dalvik.system.BaseDexClassLoader.pathList
Class baseClass = Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListField = baseClass.getDeclaredField("pathList");
pathListField.setAccessible(true);
// 3 获得宿主dexElements数组对象
PathClassLoader classLoader = (PathClassLoader)getClassLoader();
Object hostPathList = pathListField.get(classLoader);
Object[] hostDexElements = (Object[])dexElementsField.get(hostPathList);
// 4 获得插件的dexElements数组对象
String apkPath = "/sdcard/plugin.apk"; // 插件apk路径
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, getCacheDir().getAbsolutePath(), null, classLoader);
Object pluginPathList = pathListField.get(dexClassLoader);
Object[] pluginDexElements = (Object[])dexElementsField.get(pluginPathList);
// 5 合并dexElements数组对象
Object[] newDexElements = (Object[])Array.newInstance(hostDexElements.getClass().getComponentType(),
hostDexElements.length + pluginDexElements.length);
System.arraycopy(hostDexElements, 0, newDexElements, 0, hostDexElements.length);
System.arraycopy(pluginDexElements, 0, newDexElements, hostDexElements.length, pluginDexElements.length);
// 把合并后的dexElements数组对象,设置到宿主classLoader
dexElementsField.set(hostPathList, newDexElements);
} catch (Exception e) {
e.printStackTrace();
}
}