我有一些代码来获取包中看起来像这样的所有类的列表:
try {
DexFile df = new DexFile(context.getPackageCodePath());
for (Enumeration<String> iter = df.entries(); iter.hasMoreElements();) {
String s = iter.nextElement();
}
} catch (IOException e) {
e.printStackTrace();
}
但是,自从我将Android Studio升级到2.0版后,此代码已停止工作。我发现罪魁祸首是Instant Run。如果我调试应用程序,我可以看到,如果没有实例运行,DexFile变量df包含一个类名列表(超过4,000个)。当启用“即时运行”时,我只能获得30个左右的类名,而我正在寻找的类不在那里。我觉得它与multi dex有关,但我不确定Instant Run是如何工作的(我的应用程序不使用multidex)。
有没有人知道如何在启用Instant Run的情况下获取此类课程列表?或者有没有人确切知道为什么我会看到这种行为(理解它会很棒)?
答案 0 :(得分:4)
我们可以处理在应用程序数据路径中即时运行构建的DEX文件。
public class MultiDexHelper {
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final String EXTRACTED_SUFFIX = ".zip";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator +
"secondary-dexes";
private static final String PREFS_FILE = "multidex.version";
private static final String KEY_DEX_NUMBER = "dex.number";
private static SharedPreferences getMultiDexPreferences(Context context) {
return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ?
Context.MODE_PRIVATE :
Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
/**
* get all the dex path
*
* @param context the application context
* @return all the dex path
* @throws PackageManager.NameNotFoundException
* @throws IOException
*/
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper",
"getSourcePaths sourceDir=" + applicationInfo.sourceDir + ", dataDir=" + applicationInfo.dataDir);
}
List<String> sourcePaths = new ArrayList<String>();
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
//the total dex numbers
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "getSourcePaths totalDexNumber=" + totalDexNumber);
}
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" +
extractedFile.getPath() + "'");
}
}
try {
// handle dex files built by instant run
File instantRunFilePath = new File(applicationInfo.dataDir,
"files" + File.separator + "instant-run" + File.separator + "dex");
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "getSourcePaths instantRunFile exists=" + instantRunFilePath.exists() + ", isDirectory="
+ instantRunFilePath.isDirectory() + ", getAbsolutePath=" + instantRunFilePath.getAbsolutePath());
}
if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {
File[] sliceFiles = instantRunFilePath.listFiles();
for (File sliceFile : sliceFiles) {
if (null != sliceFile && sliceFile.exists() && sliceFile.isFile() && sliceFile.getName().endsWith(".dex")) {
sourcePaths.add(sliceFile.getAbsolutePath());
}
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "getSourcePaths parse instantRunFilePath exception", e);
}
return sourcePaths;
}
// /**
// * get all the classes name in "classes.dex", "classes2.dex", ....
// *
// * @param context the application context
// * @return all the classes name
// * @throws PackageManager.NameNotFoundException
// * @throws IOException
// */
// public static List<String> getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException {
// List<String> classNames = new ArrayList<String>();
// for (String path : getSourcePaths(context)) {
// try {
// DexFile dexfile = null;
// if (path.endsWith(EXTRACTED_SUFFIX)) {
// //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
// dexfile = DexFile.loadDex(path, path + ".tmp", 0);
// } else {
// dexfile = new DexFile(path);
// }
// Enumeration<String> dexEntries = dexfile.entries();
// while (dexEntries.hasMoreElements()) {
// classNames.add(dexEntries.nextElement());
// }
// } catch (IOException e) {
// throw new IOException("Error at loading dex file '" +
// path + "'");
// }
// }
// return classNames;
// }
/**
* scan parent class's sub classes
*
* @param context
* @param packageName
* @param parentClass
* @param <T>
* @return
*/
public static <T> Set<Class<? extends T>> scanClasses(Context context, String packageName, Class<T> parentClass) {
Set<Class<? extends T>> classes = new HashSet<Class<? extends T>>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (String path : getSourcePaths(context)) {
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "scanClasses path=" + path);
}
try {
DexFile dexfile = null;
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "scanClasses className=" + className);
}
if (className.toLowerCase().startsWith(packageName.toLowerCase())) {
Class clazz = classLoader.loadClass(className);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper",
"scanClasses clazz=" + clazz + ", parentClass=" + parentClass + ", equals=" + clazz
.getSuperclass().equals(parentClass));
}
if (clazz.getSuperclass().equals(parentClass)) {
classes.add(clazz);
}
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "scanClasses Error at loading dex file '" +
path + "'", e);
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "scanClasses exception", e);
}
return classes;
}
}