-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Android dynamic load classes
galen edited this page Jan 1, 2016
·
6 revisions
Android classes are loaded by 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).
public DexPathList(ClassLoader definingContext, String dexPath,
String libraryPath, File optimizedDirectory) {
...
this.dexElements =
makeDexElements(splitDexPath(dexPath), optimizedDirectory);
...
}
And then dexElements ( DexPathList$Element[] ) was created.
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".
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 expands it's dexPathList's dexElements.
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);