2016-04-08 2 views
9

я имел некоторый код, чтобы получить список всех классов в пакете, который выглядел примерно так:Android Studio 2.0 Instant Run вызывает DexFile не загружать все классы

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, содержит список имен классов (более 4000 из них). Когда Instant Run включен, я получаю только около 30 имен классов, а классы, которые я ищу, отсутствуют. У меня такое чувство, что это имеет какое-то отношение к multi dex, но я не уверен, как работает Instant Run под обложками (мое приложение не использует multidex).

Кто-нибудь знает, как я могу получить список классов, подобных этому, когда включен Instant Run? Или кто-нибудь точно знает, почему я вижу это поведение (было бы здорово это понять)?

+0

Пожалуйста, обратите внимание на это [ответ] (http://stackoverflow.com/questions/36572515/dexfile-in-2-0-versions-of-android-studio-and -gradle). InstantRun нарушил логику DexFile. – Sol

ответ

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

}

Смежные вопросы