Android不使用已废弃DexFile获取指定包下所有类的实现方案
Android平台无需DexFile获取指定包名下类列表的实现方案
实现思路:通过反射获取BaseDexClassLoader内部的dexElements列表,遍历每个dex文件的类条目,过滤出目标包名下的类即可,全程不依赖已废弃的android.util.DexFile类。
fun getClasses(context: Context, targetPackage: String): List<Class<*>> { val classList = mutableListOf<Class<*>>() val classLoader = context.classLoader as? BaseDexClassLoader ?: return emptyList() // 反射获取pathList字段 val pathListField = BaseDexClassLoader::class.java.getDeclaredField("pathList") pathListField.isAccessible = true val pathList = pathListField.get(classLoader) // 获取dexElements数组 val dexElementsField = pathList.javaClass.getDeclaredField("dexElements") dexElementsField.isAccessible = true val dexElements = dexElementsField.get(pathList) as? Array<*> ?: return emptyList() for (element in dexElements) { // 获取当前element对应的dex文件 val dexFileField = element.javaClass.getDeclaredField("dexFile") dexFileField.isAccessible = true val dexFile = dexFileField.get(element) as? dalvik.system.DexFile ?: continue // 遍历dex内的所有类名 val entries = dexFile.entries() while (entries.hasMoreElements()) { val className = entries.nextElement() if (className.startsWith(targetPackage)) { try { // 不需要排除内部类可删除以下判断 if (!className.contains("$")) { val clazz = classLoader.loadClass(className) classList.add(clazz) } } catch (e: ClassNotFoundException) { continue } catch (e: NoClassDefFoundError) { continue } } } } return classList }
注意事项
- 代码中用到的
dalvik.system.DexFile是系统内部类,不属于已废弃的公共API,目前全版本Android系统均可正常使用 - 开启混淆时需要将目标包名下的类加入混淆白名单,否则类名被混淆后无法通过包名前缀匹配
- 上述反射调用用到的内部字段均属于Android系统允许访问的灰/白名单,Android 9及以上版本无需额外配置反射豁免
- 遍历dex文件耗时较高,不要在主线程调用该方法,建议放到子线程异步执行
内容的提问来源于stack exchange,提问作者jos76
相关产品推荐
相关产品推荐

