Phoebus
Phoebus

Reputation: 321

Android dex class loader - class not found

I want to use a dex class loader to dynamically load code into my Android application. I have followed this tutorial: http://www.marioalmeida.eu/2015/01/27/how-to-easy-way-load-apk-classes-using-dexclassloader/. However I get the following error stack trace:

01-08 12:25:38.007 15147-15147/com.ph.dexloader W/System.err: java.lang.ClassNotFoundException: Didn't find class "com.ph.injection.Injection" on path: DexPathList[[zip file "/storage/emulated/0/testjars/injector.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.ph.dexloader.MainActivity.test3(MainActivity.java:153)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.ph.dexloader.MainActivity$1.onClick(MainActivity.java:34)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at android.view.View.performClick(View.java:5198)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at android.view.View$PerformClick.run(View.java:21147)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at android.os.Looper.loop(Looper.java:148)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5417)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:   Suppressed: java.io.IOException: No original dex files found for dex location /storage/emulated/0/testjars/injector.apk
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexFile.openDexFileNative(Native Method)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexFile.openDexFile(DexFile.java:295)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexFile.<init>(DexFile.java:111)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexFile.loadDex(DexFile.java:151)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexPathList.loadDexFile(DexPathList.java:282)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexPathList.makePathElements(DexPathList.java:248)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexPathList.<init>(DexPathList.java:120)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.DexClassLoader.<init>(DexClassLoader.java:57)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.ph.dexloader.MainActivity.test3(MainActivity.java:146)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:       ... 10 more
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:   Suppressed: java.lang.ClassNotFoundException: Didn't find class "com.ph.injection.Injection" on path: DexPathList[[zip file "/data/app/com.ph.dexloader-2/base.apk"],nativeLibraryDirectories=[/data/app/com.ph.dexloader-2/lib/x86, /vendor/lib, /system/lib]]
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:       ... 12 more
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:       Suppressed: java.lang.ClassNotFoundException: Didn't find class "com.ph.injection.Injection" on path: DexPathList[[dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-support-annotations-24.1.0_ae5436f3e75e322599cc28c891c7bbfb3749511e-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_9-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_8-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_7-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_6-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_5-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_4-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_3-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_2-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_1-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-slice_0-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-internal_impl-24.1.0_3cdb966acce2459aa95bcd6df4240d298e8a6aa0-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-com.android.support-support-vector-drawable-24.1.0_68483e445bfd7fe2615d6637e23aa54f7eca71ad-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-com.android.support-support-v4-24.1.0_0b01882602c790d27bb53e6d99c19f41bfa0f42f-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-com.android.support-appcompat-v7-24.1.0_ffbd2666f53be3072a55297d806cbb863283a31e-classes.dex", dex file "/data/data/com.ph.dexloader/files/instant-run/dex/slice-com.android.support-animated-vector-drawable-24.1.0_fa7ca9f78df22fc1c453fed6517df1a2457fbf73-classes.dex"],nativeLibraryDirectories=[/data/app/com.ph.dexloader-2/lib/x86, /vendor/lib, /system/lib]]
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader.findClass(IncrementalClassLoader.java:90)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at com.android.tools.fd.runtime.IncrementalClassLoader.findClass(IncrementalClassLoader.java:62)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:           ... 13 more
01-08 12:25:38.008 15147-15147/com.ph.dexloader W/System.err:           Suppressed: java.lang.ClassNotFoundException: com.ph.injection.Injection
01-08 12:25:38.009 15147-15147/com.ph.dexloader W/System.err:     at java.lang.Class.classForName(Native Method)
01-08 12:25:38.009 15147-15147/com.ph.dexloader W/System.err:     at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
01-08 12:25:38.009 15147-15147/com.ph.dexloader W/System.err:     at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
01-08 12:25:38.009 15147-15147/com.ph.dexloader W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
01-08 12:25:38.009 15147-15147/com.ph.dexloader W/System.err:               ... 14 more
01-08 12:25:38.009 15147-15147/com.ph.dexloader W/System.err:           Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

I created an Android project with no Activity and the following class that I want to load:

package com.ph.injection;

import android.util.Log;

public class Injection {

    public void inject() {
        /* this code is getting injected */
        Log.d("INJECTED", "This is injected code!");
    }
}

I generated the APK file with gradle assemble and put it into /storage/emulated/0/testjars/injector.apk on the Emulator.

This is the code I use to load the DEX file:

    String dexFile = "/testjars/injector.apk";

    // Get the path to the SD card
    File f = new File(Environment.getExternalStorageDirectory().toString() + dexFile);
    final File optimizedDexOutputPath = getDir("outdex", 0);
    DexClassLoader classLoader = new DexClassLoader(f.getAbsolutePath(), optimizedDexOutputPath.getAbsolutePath(),null, getClassLoader());

    // The classpath is created for the new class
    String completeClassName = "com.ph.injection.Injection";
    String methodToInvoke = "inject";

    try {
        Class<?> myClass = classLoader.loadClass(completeClassName);
        Object obj = (Object) myClass.newInstance();
        Method m = myClass.getMethod(methodToInvoke);
        m.invoke(obj);
    }
    catch (Exception e) {
        e.printStackTrace();
    }

I have no idea why the DEX file is not loaded correctly / the class is not found. Could anybody help me? I use a Genymotion Android 6.0.0 emulator if that matters.

Thanks!

Upvotes: 3

Views: 2584

Answers (1)

Tom Rutchik
Tom Rutchik

Reputation: 1702

Assuming that you're not running into issues not related to the DexClassLoader, such as not having permission to read and write to the external storage the problem is likely to be that you have not configured your app to be multidexenabled. In gradle you config your app as such:

defaultConfig {
  …
  multiDexEnabled true
}

If you are targeting apps below api 26 then you also have to add a dependency on the mulitdex support library. This is not necessary for api 26 and above since it already embedded in android.

If you think about it, if you are attempting to load external code, that code exists in another dex file, therefore you need your app to be multidex enabled in order for it too work. Most people only configure their app to be multidex enabled when they hit the 65K method limit for a single dex application. Accessing external code at runtime is just another reason to make your app mulitidex enabled. Note: the external code you bring in does not have to be built as multidex enabled.

If your app is already multidex enabled, then another reason that you can get the message that you class can't be found (assuming you specified the jar that contains the class you want and the class name correctly) would be that the class you want to load, requires a class that can't be found. That can be a little tricky to trace down. The error message you get is that you can't find your class, when the issue really is that it can't find a dependent class.

In my experience, solutions like "turn off Instance Run" and "disable dex optimization" are generally off track and typically only related to some particular older version, probably beta, and has since been corrected. They are referred to as aspirin solutions, cuz something and under some situations they do work, but the underlying problem still remains a mystery.

Upvotes: 1

Related Questions