5

First off I have seen Load Java-Byte-Code at Runtime and it was helpful in so far as getting to me to the same spot I'm stuck at currently.

I'm trying to load a class from a byte array to avoid storing a file on disk. For testing purpose in this example I am simply reading in a .class file into a byte array so obviously the file is still stored on disk, but it is just to see if the code can work.

I take this byte array and then use a custom ClassLoader with the method loadClass to load a Class, but it doesn't work.

    byte[] bytearray = null;
    try{    
    RandomAccessFile f = new RandomAccessFile("/sdcard/ClassToGet.dex", "r");
    bytearray = new byte[(int) f.length()];
    f.read(bytearray);

    MyClassLoader classloader = new MyClassLoader();
    classloader.setBuffer(bytearray); 
    classloader.loadClass("com.pack.ClassIWant");
    } 

Here is the ClassLoader implementation:

public class MyClassLoader extends DexClassLoader {

 private byte[] buffer;

  @Override
    public Class findClass(String className){
    byte[] b = getBuffer();
    return this.defineClass(className, b, 0, b.length);
    }

public void setBuffer(byte[] b){
    buffer = b;
}
public byte[] getBuffer(){
    return buffer;
}

And the error I am receiving is this:

java.lang.UnsupportedOperationException: can't load this type of class file at java.lang.VMClassLoader.defineClass(Native Method)

I have supplied it with .class files, .dex files, .apk, .jar, etc... I have no idea what "type of class file" it wants from me and the documentation on it is nonexistent. Any help would be great I've been trying to get this work for four days straight.

Community
  • 1
  • 1
Wait what
  • 90
  • 1
  • 8
  • http://stackoverflow.com/a/3024261/61855 – theomega Aug 04 '12 at 08:12
  • I'm still getting the same error message of "UnsupportedOperationException" even using the dex files / jar files. What did you end up doing from your example? – Wait what Aug 06 '12 at 17:29
  • Who are you adressing? Me? If you reference to my question which you linked: I was not using Android for this project, so no problems there. – theomega Aug 06 '12 at 18:59
  • Android 26 has InMemoryDexClassLoader https://developer.android.com/reference/dalvik/system/InMemoryDexClassLoader.html – Gelldur Oct 28 '17 at 11:26

3 Answers3

2

I have the same problem that you have.

The reason why I get the error "can't load this type of class file" is simple.

In the platform source, "/dalvik/vm/native/java_lang_VMClassLoader.cpp" which is related to the method "defineClass" always returns exception as follows. (version: ICS)

Finally, I reached the conclusion that I can't load .dex in byte array format.

Is there anyone who can load .dex by using byte array? (not using files)

/*
 * static Class defineClass(ClassLoader cl, String name,
 *     byte[] data, int offset, int len)
 *     throws ClassFormatError
 *
 * Convert an array of bytes to a Class object.
 */
static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args, JValue* pResult)
{
    Object* loader = (Object*) args[0];
    StringObject* nameObj = (StringObject*) args[1];
    const u1* data = (const u1*) args[2];
    int offset = args[3];
    int len = args[4];
    char* name = NULL;

    name = dvmCreateCstrFromString(nameObj);
    ALOGE("ERROR: defineClass(%p, %s, %p, %d, %d)",
        loader, name, data, offset, len);
    dvmThrowUnsupportedOperationException(
        "can't load this type of class file");

    free(name);
    RETURN_VOID();
}

/*
 * static Class defineClass(ClassLoader cl, byte[] data, int offset,
 *     int len)
 *     throws ClassFormatError
 *
 * Convert an array of bytes to a Class object. Deprecated version of
 * previous method, lacks name parameter.
 */
static void Dalvik_java_lang_VMClassLoader_defineClass2(const u4* args, JValue* pResult)
{
    Object* loader = (Object*) args[0];
    const u1* data = (const u1*) args[1];
    int offset = args[2];
    int len = args[3];

    ALOGE("ERROR: defineClass(%p, %p, %d, %d)",
        loader, data, offset, len);
    dvmThrowUnsupportedOperationException(
        "can't load this type of class file");

    RETURN_VOID();
}
  • Looking at that code it appears that defineClass ALWAYS throws the unsupported operation exception no matter what input, which is in tune to what you said. If that's the case why even allow it into the API?? – Wait what Aug 14 '12 at 17:08
1

Make sure your .dex file is a genuine dx-produced Dalvik executable and not a Java .class file in disguise. If you use the .dex extension the file must be a .dex file; otherwise use a .jar extension for a ZIP file that contains a classes.dex entry.

Not all versions of Dalvik can load classes from memory. You can work around this by loading the class from the file system. There's an example in DexMaker's generateAndLoad method:

    byte[] dex = ...

    /*
     * This implementation currently dumps the dex to the filesystem. It
     * jars the emitted .dex for the benefit of Gingerbread and earlier
     * devices, which can't load .dex files directly.
     *
     * TODO: load the dex from memory where supported.
     */
    File result = File.createTempFile("Generated", ".jar", dexCache);
    result.deleteOnExit();
    JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result));
    jarOut.putNextEntry(new JarEntry(DexFormat.DEX_IN_JAR_NAME));
    jarOut.write(dex);
    jarOut.closeEntry();
    jarOut.close();
    try {
        return (ClassLoader) Class.forName("dalvik.system.DexClassLoader")
                .getConstructor(String.class, String.class, String.class, ClassLoader.class)
                .newInstance(result.getPath(), dexCache.getAbsolutePath(), null, parent);
    } catch (ClassNotFoundException e) {
        throw new UnsupportedOperationException("load() requires a Dalvik VM", e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e.getCause());
    } catch (InstantiationException e) {
        throw new AssertionError();
    } catch (NoSuchMethodException e) {
        throw new AssertionError();
    } catch (IllegalAccessException e) {
        throw new AssertionError();
    }
Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
0

Android does not run JVM bytecode, but Dalvik bytecode. So your operation should include this line before defineClass()

context.setOptimizationLevel(-1);
tolgap
  • 9,629
  • 10
  • 51
  • 65
  • Could you go into a little more detail on what that does / how to use it? The only examples I see from a quick google search are using Rhino which I am not using. Why wouldn't loading a .dex file suffice instead of the .class? – Wait what Aug 02 '12 at 21:36
  • I looked into in some more and noticed [this](http://stackoverflow.com/questions/3022454/how-to-load-a-java-class-dynamically-on-android-dalvik) answer on SO. I bet it would help you. You will have to store it as a .dex – tolgap Aug 03 '12 at 18:23
  • I am still receiving the same Error of "UnsupportedOperationException: can't load this type of class file" for .dex files as well. I have tried .class, .dex, .apk, and .jar and none work. What am I doing wrong? – Wait what Aug 03 '12 at 20:07