Skip to content

Instantly share code, notes, and snippets.

@sr105
Created February 23, 2015 19:06
Show Gist options
  • Save sr105/504aaf344030076f40c0 to your computer and use it in GitHub Desktop.
Save sr105/504aaf344030076f40c0 to your computer and use it in GitHub Desktop.
Dynamic Library Loader for Android
package com.rdm.neocastapp;
import android.content.ContextWrapper;
import android.util.Log;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
public class DynamicLibLoader {
protected final File mDexDir;
public DynamicLibLoader(ContextWrapper contextWrapper) {
mDexDir = contextWrapper.getDir("dex", 0);
}
public DynamicLibLoader(String dexCacheDir) {
mDexDir = new File(dexCacheDir);
}
public static Class<?>[] toParameterTypes(Class<?>... parameterTypes) {
return parameterTypes;
}
public static Object[] toArgs(Object... args) {
return args;
}
// Run method "main(String[])" in fullClassName
// found in the dexed jar file, libraryFileName,
// with no command line arguments
public void runMain(String libraryFileName,
String fullClassName,
String... args) {
final Class<?>[] parameterTypes = toParameterTypes(String[].class);
//final Object[] args = toArgs(new String[0]);
runMethod(libraryFileName, fullClassName, "main", parameterTypes, args);
}
// Find methodName(parameterTypes) in fullClassName inside
// the dexed jar file, libraryFileName and run it with args
// - parameterTypes and args can be null
// - use the toParameterTypes() and toArgs() methods for convenience
public Object runMethod(String libraryFileName,
String fullClassName,
String methodName,
Class<?>[] parameterTypes,
Object[] args) throws IllegalArgumentException {
try {
@SuppressWarnings("unchecked")
final Class<Object> classToLoad = (Class<Object>) getClass(libraryFileName, fullClassName);
final Object instance = classToLoad.newInstance();
final Method main = classToLoad.getMethod(methodName, parameterTypes);
//final String[] args = new String[0];
//main.invoke(instance, (Object) args);
return main.invoke(instance, args);
} catch (InstantiationException | IllegalAccessException |
NoSuchMethodException | InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
}
// Find methodName(parameterTypes) in fullClassName inside
// the dexed jar file, libraryFileName and run it with args
// - parameterTypes and args can be null
// - use the toParameterTypes() and toArgs() methods for convenience
public Object newInstance(String libraryFileName,
String fullClassName) throws IllegalArgumentException {
try {
@SuppressWarnings("unchecked")
final Class<Object> classToLoad = (Class<Object>) getClass(libraryFileName, fullClassName);
return classToLoad.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
public final Class<?> getClass(String libraryFileName,
String fullClassName) throws IllegalArgumentException {
try {
deleteCachedDexFiles(libraryFileName);
Log.i("DLL", "exists? " + new File(libraryFileName).exists());
final DexClassLoader classLoader = new DexClassLoader(
libraryFileName, mDexDir.getAbsolutePath(), null,
this.getClass().getClassLoader());
return classLoader.loadClass(fullClassName);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
// Note: About re-loading a jar file in the same process' lifetime
//
// If we try to load the same jar file by name,
// but not by content (different file, same name)
// as previously loaded in this current process,
// this process will crash with a SIGBUS error.
// While setIntentRedelivery(true) above *will*
// cause Android to restart us and re-send the
// current Intent, this is not very graceful.
// Instead, it seems that if you clear out the
// dex cache, all is good.
//
// TODO: Only delete the cache file for the library we're loading
void deleteCachedDexFiles(String libraryFileName) {
String libName = new File(libraryFileName).getName();
if (libName.endsWith(".apk") || libName.endsWith(".jar"))
libName = libName.substring(0, libName.length() - 4);
Log.i("DynamicLibLoader", "deleteCacheDexFiles(" + libName + ") from " + mDexDir.getAbsolutePath());
for (File f : mDexDir.listFiles()) {
Log.i("DLL", "Dex File: " + f.getName());
// only delete the dex file matching the library file name?
if (f.isFile() && f.getName()
.endsWith(".dex")) {
f.delete();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment