2014-10-29 66 views
2

我想用ApplicationWrapepr方法在我的應用程序中使用multidex喜歡這裏描述https://plus.google.com/104023661970539138053/posts/YTMf8ADTcFg的Android Multidex列表中的所有類

我用--minimal - 主 - DEX選項,像這樣一個保持文件一起:

android/support/multidex/ZipUtil.class 
android/support/multidex/ZipUtil$CentralDirectory.class 
android/support/multidex/MultiDex$V14.class 
android/support/multidex/MultiDexExtractor$1.class 
android/support/multidex/MultiDexExtractor.class 
com/<mypackage>/common/MyApplication.class 
com/<mypackage>/common/MyApplicationWrapper.class 
com/<mypackage>/common/ui/DashboardActivity.class 
android/support/multidex/MultiDexApplication.class 
android/support/multidex/MultiDex.class 
android/support/multidex/MultiDex$V19.class 
android/support/multidex/MultiDex$V4.class 

這會導致列出的類在我的主要dex文件中沒有問題。我比使用一個庫,使用下面的代碼來列出dexfile中的所有類,但只獲取主「clesses.dex」的條目,而不是所有其他加載的dex文件的條目,因爲新的DexFile只檢查「classes.dex」 :

private static List getPaths(final String [] sourcePaths){ List result = new ArrayList();

for (String s : sourcePaths) { 
    try { 
    DexFile dexfile = new DexFile(s); 
    Enumeration<String> entries = dexfile.entries(); 

    while (entries.hasMoreElements()) { 
     result.add(entries.nextElement()); 
    } 
    } catch (IOException ioe) { 
    Log.w(TAG, "cannot open file=" + s + ";Exception=" + ioe.getMessage()); 
    } 
} 

return result; 
} 

了現在單一路徑獲取與確定:

application.getApplicationContext().getApplicationInfo().sourceDir; 

導致財產以後要像/data/../myapplicationname.apk

是否有另一種可能性,讓所有班列出的dex文件?或者ClassLoaders中的所有類?該庫對於項目來說至關重要,並且使用這種方法通過稍後的反射來查找組件實現。

EDIT1: 如果發現該classes2.dex文件被劃歸: /data/data/com./code_cache/secondary-dexes/com.-1.apk.classes2.dex

但是,在使用此路徑使用新的DexFile()時,將引發IOEsxception,並顯示消息「無法打開dexfile」。

回答

5

DexFile接受zip/apk文件的路徑,並將其解壓縮以找到.dex文件。 所以如果你使用.dex作爲路徑,它會拋出一個錯誤。

此外Google也發佈了一篇文章Building Apps with Over 65K Methods來解決--multi-dex問題。

我寫了一個類來加載所有的類。你可以讀到更多在:如果您用的即時運行的應用程序http://xudshen.info/2014/11/12/list-all-classes-after-multidex/

import android.content.Context; 
import android.content.SharedPreferences; 
import android.content.pm.ApplicationInfo; 
import android.content.pm.PackageManager; 
import android.os.Build; 

import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.Enumeration; 
import java.util.List; 

import dalvik.system.DexFile; 

/** 
* Created by [email protected] on 14/11/13. 
*/ 
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); 

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

     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() + "'"); 
      } 
     } 

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

喜。我認爲這會奏效。由於性能考慮,我們決定使用顯式依賴配置。 – Denny1989 2014-11-18 13:59:51

+0

它在哪裏可以在這個解決方案中失去性能? – 2015-03-29 19:51:46

1

,DEX文件路徑需要補充一點:

// handle dex files built by instant run 
     File instantRunFilePath = new File(applicationInfo.dataDir, 
              "files" + File.separator + "instant-run" + File.separator + "dex"); 

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