Here is a complete program to demonstrate class loading in standard desktop Java:
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.Method;
public class Main {
private void load(String filename, String classname) {
try {
URL[] urls = { new URL(filename) };
ClassLoader parent = getClass().getClassLoader();
ClassLoader loader = new URLClassLoader(urls, parent);
Class<?> c = loader.loadClass(classname);
Object o = c.newInstance();
Method m = c.getMethod("func");
m.invoke(o);
} catch (Exception e) {
System.err.print(String.format("DLL failed: %s: %s",
e.getClass().getName(), e.getMessage()));
}
}
public static void main(String [ ] args) {
Main main = new Main();
main.load("file:/tmp/Foo.jar", "Foo");
}
}
This will load class Foo from /tmp/Foo.jar, create an instance of that class, and finally call the member function func().Note:
- Loading of a regular file is done via URLClassLoader.
- The file name must be prefixed by "file:".
- The statements in the try clause throws a number of different exceptions, that are all caught by the catch-all Exception.
$ javac *.java
$ cp Foo.jar /tmp
$ java Main
Dynamic class loading in Android is slightly different:- The standard class loaders don't seem to work.
- The class loader to use is dalvik.system.DexClassLoader.
- The classes must be put into a .dex file that must be embedded in a .jar file. Done using the little known utility dx.
package se.sdu;
import java.lang.reflect.Method;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import dalvik.system.DexClassLoader;
public class Main extends Activity {
public void dload(String filename, String classname) {
try {
Context ctx = getApplicationContext();
String dex_dir = ctx.getDir("dex", 0).getAbsolutePath();
ClassLoader parent = getClass().getClassLoader();
DexClassLoader loader = new DexClassLoader(filename, dex_dir, null, parent);
Class c = loader.loadClass(classname);
Object o = c.newInstance();
Method m = c.getMethod("func");
m.invoke(o);
} catch (Exception e) {
Log.e("se.sdu", String.format("DLL failed: %s: %s",
e.getClass().getName(), e.getMessage()));
}
}
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dload("/mnt/sdcard/tmp/Foo.jar", "Foo");
}
}
Note
- dex_dir is as a cache for optimized classes.
- The file name must not be prefixed by "file:".
Now to the dynamically loaded class:
import android.util.Log;
public class Foo {
public static void func() {
Log.d("se.sdu", "Hello from Foo.func()");
}
}
To compile the dynamically loadable class, and build and push the jar file containing it:javac -classpath /path/to/android.jar Foo.java
dx --dex --output Foo.jar Foo.class
adb push Foo.jar /mnt/sdcard/tmp/.
The option --dex tell dx to generate a .dex file, and the file name extension .jar to put the .dex file into a .jar file.That's all.
How about making Proxy for class?
ReplyDeleteHow about making Proxy for class?
ReplyDeleteHow can I load a jar in app from a server say my jar is at http://abc.com/mydex.jar.
ReplyDeleteI tried using URLClassLoader, it gives me ClassNotFoundException. I guess, this is because URLClassLoader is unable to find the class because the jar has classes.dex in it. Via dexClassLoader also, getting the same exception.
Can you please help, how to load the dex jar from server?
Have you found a solution please? I've a same problem.
Delete