【问题标题】:How to load a class from a byte array in android?如何从android中的字节数组加载一个类?
【发布时间】:2012-08-02 21:01:23
【问题描述】:

首先我看到了Load Java-Byte-Code at Runtime,它对我到达我目前遇到的同一个地方很有帮助。

我正在尝试从字节数组加载类以避免将文件存储在磁盘上。在本例中,出于测试目的,我只是将 .class 文件读入字节数组,因此显然该文件仍存储在磁盘上,但这只是为了查看代码是否可以工作。

我取这个字节数组,然后使用自定义的 ClassLoader 和 loadClass 方法来加载一个 Class,但它不起作用。

    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");
    } 

这是 ClassLoader 的实现:

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;
}

我收到的错误是这样的:

java.lang.UnsupportedOperationException: 无法加载这种类型的类文件 在 java.lang.VMClassLoader.defineClass(Native Method)

我已经为它提供了 .class 文件、.dex 文件、.apk、.jar 等...我不知道它想要从我这里得到什么“类文件类型”,而且它的文档也不存在。任何帮助都会很棒我一直试图连续四天完成这项工作。

【问题讨论】:

标签: java android dynamic classloader


【解决方案1】:

我和你有同样的问题。

我得到错误“无法加载这种类型的类文件”的原因很简单。

在平台源码中,与方法“defineClass”相关的“/dalvik/vm/native/java_lang_VMClassLoader.cpp”总是返回如下异常。 (版本:ICS)

最后得出结论,无法加载字节数组格式的.dex。

有没有人可以使用字节数组加载.dex? (不使用文件)

/*
 * 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();
}

【讨论】:

  • 查看该代码,无论输入什么,defineClass 似乎总是抛出不受支持的操作异常,这与您所说的一致。如果是这样,为什么还要允许它进入 API??
【解决方案2】:

确保您的 .dex 文件是真正的 dx 生成的 Dalvik 可执行文件,而不是伪装的 Java .class 文件。如果您使用.dex 扩展名,则文件必须是.dex 文件;否则,对包含 classes.dex 条目的 ZIP 文件使用 .jar 扩展名。

并非所有版本的 Dalvik 都可以从内存中加载类。您可以通过从文件系统加载类来解决此问题。 DexMaker'sgenerateAndLoad方法中有一个例子:

    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();
    }

【讨论】:

    【解决方案3】:

    Android 不运行 JVM 字节码,而是运行 Dalvik 字节码。所以你的操作应该在defineClass()之前包含这一行

    context.setOptimizationLevel(-1);
    

    【讨论】:

    • 您能否详细介绍一下它的作用/如何使用它?我从快速谷歌搜索中看到的唯一例子是使用我没有使用的 Rhino。为什么加载 .dex 文件而不是 .class 就足够了?
    • 我查看了更多内容并注意到this SO 上的答案。我敢打赌它会帮助你。您必须将其存储为 .dex
    • 我仍然收到相同的错误“UnsupportedOperationException:无法加载这种类型的类文件”对于 .dex 文件也是如此。我尝试过 .class、.dex、.apk 和 .jar,但都没有。我做错了什么?
    猜你喜欢
    • 1970-01-01
    • 2017-12-08
    • 1970-01-01
    • 1970-01-01
    • 2019-05-13
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    相关资源
    最近更新 更多