Created
February 22, 2011 17:16
-
-
Save marshall/839003 to your computer and use it in GitHub Desktop.
A reflection hack to override the APK ClassLoader so you can launch Activities in an external JAR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// !!WARNING: Not recommended for production code!! | |
// | |
public class ClassLoaderActivity extends Activity | |
{ | |
public void onCreate(Bundle savedInstanceState) | |
{ | |
// file.jar has a dex'd "classes.dex" entry that you can generate with "dx" from any number of JARs or class files | |
ClassLoader dexLoader = new DexClassLoader("/path/to/file.jar", getCacheDir().getAbsolutePath(), null, getClassLoader()); | |
setAPKClassLoader(dexLoader); | |
try { | |
Class<?> activityClass = dexLoader.loadClass("com.company.MyActivity"); | |
Intent intent = new Intent(this, activityClass); | |
startActivity(intent); | |
} catch (ClassNotFoundException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
finish(); | |
} | |
private void setAPKClassLoader(ClassLoader classLoader) | |
{ | |
try { | |
Field mMainThread = getField(Activity.class, "mMainThread"); | |
Object mainThread = mMainThread.get(this); | |
Class threadClass = mainThread.getClass(); | |
Field mPackages = getField(threadClass, "mPackages"); | |
HashMap<String,?> map = (HashMap<String,?>) mPackages.get(mainThread); | |
WeakReference<?> ref = (WeakReference<?>) map.get(getPackageName()); | |
Object apk = ref.get(); | |
Class apkClass = apk.getClass(); | |
Field mClassLoader = getField(apkClass, "mClassLoader"); | |
mClassLoader.set(apk, classLoader); | |
} catch (IllegalArgumentException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} catch (IllegalAccessException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
private Field getField(Class<?> cls, String name) | |
{ | |
for (Field field: cls.getDeclaredFields()) | |
{ | |
if (!field.isAccessible()) { | |
field.setAccessible(true); | |
} | |
if (field.getName().equals(name)) { | |
return field; | |
} | |
} | |
return null; | |
} | |
} |
Please note that if your are loading a dex file that calls a native function, you should specify the path to the shared library containing that function as the third parameter of DexClassLoader
constructor, otherwise the runtime will report UnsatisfiedLinkError
. Besides, you need to load the shared library after your dex file is loaded.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
it's also possible to do so directly in
Application
class. we can invoke android.app.ActivityThread.currentActivityThread() to getmainThread
. here is the code: