Android- Using DexClassLoader to load apk file-ThrowExceptions

Exception or error:

I’ve hit a bit of a wall. Any help would be appreciated. I have an app that I want to use DexClassLoader to load another apk file.

Here is my code:

DexClassLoader dLoader = new DexClassLoader("/sdcard/download/test.apk","/sdcard/download",null,ClassLoader.getSystemClassLoader().getParent());
Class calledClass = dLoader.loadClass("com.test.classname");
Intent it=new Intent(this, calledClass);
it.setClassName("com.test", "com.test.classname");
startActivity(it);

Now I had already installed test.apk so when I ran the above code it
worked fine and launched the application. However I want to be able to
run this without test.apk being installed already (as that would
defeat the entire point of the application) . So I uninstalled it and
when I ran the my app again I get this error:

android.content.ActivityNotFoundException: Unable to find explicit
activity class {com.test/com.test.classname}; have you declared this
activity in your AndroidManifest.xml.

So I’m a bit stumped here. This activity is declared in the Manifest
of the apk I am trying to run. I can’t declare it in my applications
Manifest. Any ideas?

Thanks,
Craig

How to solve:

Try using Android’s PathClassLoader:

    String packagePath = "com.mypackage";
    String classPath = "com.mypackage.ExternalClass";

    String apkName = null;
    try {
        apkName = getPackageManager().getApplicationInfo(packagePath,0).sourceDir;
    } catch (PackageManager.NameNotFoundException e) {
        // catch this
    }

    // add path to apk that contains classes you wish to load
    String extraApkPath = apkName + ":/path/to/extraLib.apk" 

    PathClassLoader pathClassLoader = new dalvik.system.PathClassLoader(
            apkName,
            ClassLoader.getSystemClassLoader());

    try {
        Class<?> handler = Class.forName(classPath, true, pathClassLoader);
    } catch (ClassNotFoundException e) {
        // catch this
    }

###

Although the question is old, I will answer because I struggled a bit to find a clear answer for your same question for myself. First, I would like to highlight that a clear requirement in your question is to load a class from an .apk that is not already installed on the device. Therefore, calling the package manager using getPackageManager() and providing it with the package path will clearly lead to NameNotFoundException because the .apk that has the package is not installed on the device.

So, the way to go about loading classes from an .apk file that is not installed on the device (i.e. you only have the .apk stored in a directory on your SDCARD) is by using DexClassLoader as follows:

1- Make sure you have the .apk file in a directory on your SDCARD. I’ve mine Offloadme.apk in the Download folder on my SDCARD.

2- Add read permission in your AndroidManifest.xml to allow your app to read from the manifest.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

3- Use the following definitions to define the path of the .apk, the class name inside the apk, and method name in that class that you would like to invoke:

final String apkFile =Environment.getExternalStorageDirectory().getAbsolutePath()+"/Download/Offloadme.apk";
String className = "com.khaledalanezi.offloadme.SimpleCalculator";
String methodToInvoke = "add"; 

4- Use the DexClassLoader to load the .apk and call the add method in the SimpleCalculator class using reflection as follows:

    final File optimizedDexOutputPath = getDir("outdex", 0);

    DexClassLoader dLoader = new DexClassLoader(apkFile,optimizedDexOutputPath.getAbsolutePath(),
            null,ClassLoader.getSystemClassLoader().getParent());

    try {
        Class<?> loadedClass = dLoader.loadClass(className);
        Object obj = (Object)loadedClass.newInstance();
        int x =5;
        int y=6;
        Method m = loadedClass.getMethod(methodToInvoke, int.class, int.class);
        int z = (Integer) m.invoke(obj, y, x);              
        System.out.println("The sum of "+x+" and "+"y="+z);

    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Note that in my simple example, I added two numbers using the add method available in the SimpleCalculator class loaded from the Offloadme.apk file stored on my SDCARD and I was able to print the correct answer which is 11.

###

You can’t do that. Even if you’re able to access classes from external file, Android still does not know them. And you don’t run activities directly, but by requesting Android to run them, so they have to be registered/installed into system.

Leave a Reply

Your email address will not be published. Required fields are marked *