DexFile在2.0版本的Android Studio和Gradle中

时间:2016-04-12 11:53:17

标签: android android-studio android-gradle

我使用以下代码来实例化某个包中包含的所有类。

DexFile df = new DexFile(getPackageCodePath());
for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) {
    String className = iter.nextElement();
    if (className.contains(packageName) && !className.contains("$")) {
        myClasses.add(Class.forName(className).newInstance());
    }
}

不幸的是它不再正常工作了。从Android Studio 2和Gradle 2.0.0开始,DexFile条目不再包含应用程序中的所有类,而只包含属于com.android.tools包的类。

这是一个已知问题吗?

3 个答案:

答案 0 :(得分:7)

此问题与Gradle 2.0.0的Android插件中的新InstantRun功能相关。

getPackageCodePath()获取指向Android文件系统中base.apk文件的String。如果我们解压缩该apk,我们可以在其根文件夹中找到一个或多个.dex文件。从方法df.entries()获取的条目迭代在该根文件夹中找到的.dex文件,以获取其所有已编译的类。

但是,如果我们使用新的Android插件for Gradle,我们只会找到与Android运行时和即时运行相关的.dex(包com.tools.android.fd.runtimecom.tools.android.fd.commoncom.tools.android.tools.ir.api )。每个其他类都将编译成几个.dex文件,压缩到一个名为instant-run.zip的文件中,并放入apk的根文件夹中。

这就是为什么问题中发布的代码无法列出应用程序中的所有类。但是,这只会影响Debug版本,因为Release版本不具备InstantRun。

答案 1 :(得分:1)

要访问所有DexFile,您可以执行此操作

internal fun getDexFiles(context: Context): List<DexFile> {
    // Here we do some reflection to access the dex files from the class loader. These implementation details vary by platform version,
    // so we have to be a little careful, but not a huge deal since this is just for testing. It should work on 21+.
    // The source for reference is at:
    // https://android.googlesource.com/platform/libcore/+/oreo-release/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
    val classLoader = context.classLoader as BaseDexClassLoader

    val pathListField = field("dalvik.system.BaseDexClassLoader", "pathList")
    val pathList = pathListField.get(classLoader) // Type is DexPathList

    val dexElementsField = field("dalvik.system.DexPathList", "dexElements")
    @Suppress("UNCHECKED_CAST")
    val dexElements = dexElementsField.get(pathList) as Array<Any> // Type is Array<DexPathList.Element>

    val dexFileField = field("dalvik.system.DexPathList\$Element", "dexFile")
    return dexElements.map {
        dexFileField.get(it) as DexFile
    }
}

private fun field(className: String, fieldName: String): Field {
    val clazz = Class.forName(className)
    val field = clazz.getDeclaredField(fieldName)
    field.isAccessible = true
    return field
}

答案 2 :(得分:0)

用于获取使用以下方法的应用程序的所有dex文件。

public static ArrayList<DexFile> getMultiDex()
{
    BaseDexClassLoader dexLoader = (BaseDexClassLoader) getClassLoader();
    Field f = getField("pathList", getClassByAddressName("dalvik.system.BaseDexClassLoader"));
    Object pathList = getObjectFromField(f, dexLoader);
    Field f2 = getField("dexElements", getClassByAddressName("dalvik.system.DexPathList"));
    Object[] list = getObjectFromField(f2, pathList);
    Field f3 = getField("dexFile", getClassByAddressName("dalvik.system.DexPathList$Element"));

    ArrayList<DexFile> res = new ArrayList<>();

    for(int i = 0; i < list.length; i++)
    {
        DexFile d = getObjectFromField(f3, list[i]);
        res.add(d);
    }

    return res;
}

// ------------其他方法

public static ClassLoader getClassLoader()
{
    return Thread.currentThread().getContextClassLoader();
}


public static Class<?> getClassByAddressName(String classAddressName)
{
    Class mClass = null;
    try
    {
        mClass = Class.forName(classAddressName);
    } catch(Exception e)
    {
    }
    return mClass;
}


public static <T extends Object> T getObjectFromField(Field field, Object arg)
{
    try
    {
        field.setAccessible(true);
        return (T) field.get(arg);
    } catch(Exception e)
    {
        e.printStackTrace();
        return null;
    }
}