Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A reflection hack to override the APK ClassLoader so you can launch Activities in an external JAR.
//
// !!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;
}
}
@fwelto
Copy link

fwelto commented May 11, 2013

very interesting! Can you comment a bit more on the format of the "file.jar has a dex'd "classes.dex" entry" that you mention in the code snippet? Since the classes.dex file only contains byte-code, where do the resource files (layout, string, etc) associated with the Activity being launched reside?

@nickcaballero
Copy link

nickcaballero commented Nov 21, 2013

Hey I wrote my own hack for this (https://gist.github.com/nickcaballero/7045993) and was wondering how reliable this one is? Have you found any issues using this approach?

@dawormmastermind
Copy link

dawormmastermind commented May 7, 2014

Hey marshall, this is exactly what I am trying to do.

I tried running your code but I always get the exception: class resolved by unexpected dex.

Do you have any Idea why this is? Did this code work for an external apk signed by a different developer?

I would really appreciate some feedback. Thanks

@ndahlquist
Copy link

ndahlquist commented Jan 3, 2015

Hi @marshall, it appears that Android switched HashMap on line 31 to an ArrayMap. Please consider this revision to work with both versions of the Android OS: https://gist.github.com/ndahlquist/19867c60ca4a6e7c1ca1/revisions

@ysmreg
Copy link

ysmreg commented Oct 10, 2020

To use this code, list the activity that does not exist in the Android Manifest of the app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment