How to load a dex file using dex DexClassLoader?

1k Views Asked by At

I am trying to load dynamically a dex file that contains android activities classes using DexClassLoader class using the following code.

try {
File file = File.createTempFile("input",".dex");
InputStream reader = getResources().openRawResource(R.raw.classes);
OutputStream writer = new FileOutputStream(file.getAbsolutePath());

while(true){ int x = reader.read(); if(x == -1) break; writer.write(x);}

DexClassLoader loader = new DexClassLoader(file.getAbsolutePath(),null,null,MainActivity.this.getClassLoader());

Class<?> c = loader.loadClass("com.example.myapplication2.MainActivity");
Intent intent = new Intent(MainActivity.this.getApplicationContext(),c);
startActivity(intent);
}

catch(Exception e){
e.printStackTrace();
}

However, After execution of the previous code, The application crash with the following exception

android.content.ActivityNotFoundException: Unable to find explicit activity class{com.example.myapplication/com.example.myapplication2.MainActivity}; have you declared this activity in your AndroidManifest.xml?
 at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2005)
 at android.app.Instrumentation.execStartActivity(Instrumentation.java:1673)
 at android.app.Activity.startActivityForResult(Activity.java:4586)
 at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:728)
 at android.app.Activity.startActivityForResult(Activity.java:4544)
 at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:709)
 at android.app.Activity.startActivity(Activity.java:4905)
 at android.app.Activity.startActivity(Activity.java:4873)
 at com.example.myapplication.MainActivity$1.onClick(MainActivity.java:82)
 at android.view.View.performClick(View.java:6597)
 at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1219)
 at android.view.View.performClickInternal(View.java:6574)
 at android.view.View.access$3100(View.java:778)
 at android.view.View$PerformClick.run(View.java:25885)
 at android.os.Handler.handleCallback(Handler.java:873)
 at android.os.Handler.dispatchMessage(Handler.java:99)
 at android.os.Looper.loop(Looper.java:193)
 at android.app.ActivityThread.main(ActivityThread.java:6669)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Also, I noticed that most of examples the dex file contain normal java classes not activity classes so I wonder if what I am trying to do is applicable or not and the reason beyond the exception

1

There are 1 best solutions below

0
oviche On

The following solution worked for me and it's inspired from the blog post

File file = File.createTempFile("input",".dex");
          InputStream reader = getResources().openRawResource(R.raw.classes);
          OutputStream writer = new FileOutputStream(file.getAbsolutePath());

while(true){ int x = reader.read(); if(x == -1) break; writer.write(x);}

Context context = this;
DexClassLoader dexloader = new DexClassLoader(file.getAbsolutePath()
                             ,null,
                              null,
                              context.getClassLoader());

Field f = BaseDexClassLoader.class.getDeclaredField("pathList");
f.setAccessible(true);
Object dexPathObjNew = f.get(dexloader); // get the pathList of dexloader that hold the loaded dex file path 
f.set(context.getClassLoader(), dexPathObjNew); // set the pathlist of current context classloader to the pathList of dexloader
Class c = context.getClassLoader().loadClass("com.example.myapplication2.helloActivity");

Intent i = new Intent(context,c);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);

Now the class loader of current context can see the loaded dex.