-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Android dynamic load classes
Android classes are loaded by DexClassLoader.
Android类由DexClassLoader加载
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
It was simply redirect to class BaseDexClassLoader.
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(parent);
this.originalPath = dexPath;
this.pathList =
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
Let's track the dexPath, as usual, it's "/data/../*.apk", now it was used to create the pathList (DexPathList).
追踪下参数 dexPath,这个鬼通常是"/data/../*.apk",现用作创建pathList
public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
...
this.dexElements =
makeDexElements(splitDexPath(dexPath), optimizedDirectory);
...
}
And then dexElements ( DexPathList$Element[] ) was created.
再创建dexElements,注意下面代码里的 Archive File Loading Block,压缩包加载代码块
private static final String DEX_SUFFIX = ".dex";
private static final String JAR_SUFFIX = ".jar";
private static final String ZIP_SUFFIX = ".zip";
private static final String APK_SUFFIX = ".apk";
...
private static Element[] makeDexElements(ArrayList<File> files,
File optimizedDirectory) {
ArrayList<Element> elements = new ArrayList<Element>();
/*
* Open all files and load the (direct or contained) dex files
* up front.
*/
for (File file : files) {
ZipFile zip = null;
DexFile dex = null;
String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
...
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
|| name.endsWith(ZIP_SUFFIX)) {
//---------------------------------------------------
// Archive File Loading Block
// if (name.endsWith(".so") doFollowingWithReflect();
//---------------------------------------------------
try {
zip = new ZipFile(file);
} catch (IOException ex) {
/*
* Note: ZipException (a subclass of IOException)
* might get thrown by the ZipFile constructor
* (e.g. if the file isn't actually a zip/jar
* file).
*/
System.logE("Unable to open zip file: " + file, ex);
}
try {
dex = loadDexFile(file, optimizedDirectory);
} catch (IOException ignored) {
/*
* IOException might get thrown "legitimately" by
* the DexFile constructor if the zip file turns
* out to be resource-only (that is, no
* classes.dex file in it). Safe to just ignore
* the exception here, and let dex == null.
*/
}
} else {
System.logW("Unknown file type for: " + file);
}
if ((zip != null) || (dex != null)) {
elements.add(new Element(file, zip, dex));
}
}
return elements.toArray(new Element[elements.size()]);
}
By now, we can see that DexClassLoader only support files with extensions ".dex", ".jar", ".zip" and ".apk".
可以看到,DexClassLoader不支持".so"后缀
For support ".so" file which can be automatic built in android application storage, we needs to do stuff as Archive File Loading Block to create a DexPathList$Element and then reflect on host class loader and expand it's dexPathList's dexElements.
为了让应用启动时能自动复制插件包到应用存储目录,需要支持".so"后缀。做法就是模拟 压缩包加载代码块,创建一个dex元素,再反射添加到宿主class loader里的dexPathList。
Following is the fake code:
伪代码:
Context context = getApplicationContext();
File plugin = new File(context.getApplicationInfo().dataDir, "lib/**.so");
Element element = makeDexElement(plugin); // dalvik.system.DexPathList$Element
context.getClassLoader() // dalvik.system.DexClassLoader
.@dexPathList // dalvik.system.DexPathList
.@dexElements // dalvik.system.DexPathList$Element []
.insert(element, 0);