I'm doing some testing with DexClassLoader, to see if it's possible to "update" my app with new functionality. Currently I've got this method for testing, which works with testClass and running the test() method.. What I would like to know, is how could I "replace" or "update" and already existing class.. ? Any ideas or other suggestions are welcome.
Method:
public static void loadExtLib(String pathDexFile, Context ctx) {
String temp = ctx.getDir("temp", 0).toString();
DexClassLoader classLoader = new DexClassLoader(pathDexFile,
temp, null, ctx.getClass().getClassLoader());
String testClass = "com.mystudio.myapp.TestClass";
Class<?> libProviderClass = null;
try {
libProviderClass = classLoader.loadClass(testClass);
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
try {
Method m = libProviderClass.getDeclaredMethod("test", null);
try {
m.invoke(libProviderClass, null);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
}
Log.v("loadClass", "loaded: " + testClass);
}
You can't "replace" or "update" a class generally; only classes loaded by the
DexClassLoader
or one of its child loaders will be able to see the loaded class at all. You cannot, for example, replacejava.lang.String
with a version that does nefarious things to circumvent security restrictions in a class.This somewhat reduces the utility of loading multiple classes with the same name. On the other hand, it ensures that the actions of classloader A are independent of the actions of classloader B, so long as one isn't a parent of another.
One possibly useful approach to working with runtime-loaded classes is to have an interface defined in the base loader that is implemented by both "old" and "new" versions of the class. By referring to instances of the classes through the interface you avoid the fact that "old" code can't see the "new" class definition.
(The best way to think about this is to mentally tag every class with its classloader. Then it's obvious that you can't pass a
String'new
to a function written forString'old
, but if they both implementCharSequence'old
then you can always refer to them through that.)There's a bit of code in the Dalvik tests that exercises runtime "replacement", and may be useful to read through. See the 068-classloader tests. The contents of
src
andsrc-ex
are built into separate .jar files; the latter is loaded at runtime.