我使用以下代码来实例化某个包中包含的所有类。
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
包的类。
这是一个已知问题吗?
答案 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.runtime
,com.tools.android.fd.common
和com.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;
}
}