¶¯Ì¬¼ÓÔØ¼¼Êõ(²å¼þ»¯)ϵÁÐÒѾ¿ÓÁËÓÐÒ»¶Îʱ¼äÁË£¬²»¹ýUPÖ÷ÎÒ²¢Ã»ÓÐ·ÅÆúÖÎÁƹþ£¬ÏàÐÅÔÚ²»¾ÍµÄδÀ´¾Í¿ÉÒÔ¿´µ½¡°ÏµÍ³Api
Hookģʽ¡±ºÍ²å¼þ»¯¿ò¼ÜFrontiaµÄ¸üÐÂÁË¡£½ñÌìÒª½²µÄÊǶ¯Ì¬¼ÓÔØ¼¼ÊõµÄÇ×ÆÝ ¡ª¡ª MultiDex¡£ËûÃǵĺËÐÄÔÀíÖ®Ò»¶¼ÊÇdexÎļþµÄ¼ÓÔØ¡£
MultiDexÊÇGoogleΪÁ˽â¾ö¡°65535·½·¨Êý³¬±ê¡±ÒÔ¼°¡°INSTALL_FAILED_DEXOPT¡±ÎÊÌâ¶ø¿ª·¢µÄÒ»¸öSupport¿â£¬¾ßÌåÈçºÎʹÓÃMultiDexÏÖÔÚÊÐÃæÒѾÓÐÒ»´ó¶Ñ½Ì³Ì(¿ÉÒԲο¼¸ø
App ÆôÓà MultiDex ¹¦ÄÜ)£¬ÕâÀï²»ÔÙ׸Êö¡£ÕâÆªÈÕÖ¾Ö÷ÒªÊÇÅäºÏÔ´Âë·ÖÎöMultiDexµÄ¹¤×÷ÔÀí£¬ÒÔ¼°ÌṩһЩMultiDexÓÅ»¯µÄ·½°¸¡£
DexµÄ¹¤×÷»úÖÆ
µÈµÈ£¬Õâ¸öÕ½ڽ²µÄ²»ÊÇMultiDexÂð£¬Ôõô±ä³ÉDexÁË?û´í¹þ£¬Ã»ÓÐDex£¬ÄÄÀ´µÄMultiDex¡£ÔÚAndroidÖУ¬¶ÔDexÎļþ²Ù×÷¶ÔÓ¦µÄÀà½Ð×öDexFile¡£ÔÚCLASSLOADER
µÄ¹¤×÷»úÖÆÖУ¬ÎÒÃÇ˵µ½£º
¶ÔÓÚ Java ³ÌÐòÀ´Ëµ£¬±àд³ÌÐò¾ÍÊDZàдÀ࣬ÔËÐгÌÐòÒ²¾ÍÊÇÔËÐÐÀà(±àÒëµÃµ½µÄclassÎļþ)£¬ÆäÖÐÆðµ½¹Ø¼ü×÷ÓõľÍÊÇÀà¼ÓÔØÆ÷
ClassLoader¡£
Android³ÌÐòµÄÿһ¸öClass¶¼ÊÇÓÉClassLoader#loadClass·½·¨¼ÓÔØ½øÄÚ´æµÄ£¬¸ü׼ȷÀ´Ëµ£¬Ò»¸öClassLoaderʵÀý»áÓÐÒ»¸ö»òÕß¶à¸öDexFileʵÀý£¬µ÷ÓÃÁËClassLoader#loadClassÖ®ºó£¬ClassLoader»áͨ¹ýÀàÃû£¬ÔÚ×Ô¼ºµÄDexFileÊý×éÀïÃæ²éÕÒÓÐûÓÐÄǸöDexFile¶ÔÏóÀïÃæ´æÔÚÕâ¸öÀ࣬Èç¹û¶¼Ã»ÓоÍÅ×ClassNotFoundÒì³£¡£ClassLoaderͨ¹ýµ÷ÓÃDexFileµÄÒ»¸ö½ÐdefineClassµÄNative·½·¨È¥¼ÓÔØÖ¸¶¨µÄÀ࣬ÕâµãÓëJVMÂÔÓв»Í¬£¬ºóÕßÊÇÖ±½Óµ÷ÓÃClassLoader#defineCLass·½·¨£¬·´Õý×îºóʵ¼Ê¼ÓÔØÀàµÄ·½·¨¶¼½ÐdefineClass¾Íû´íÁË¡£
´´½¨DexFile¶ÔÏó
Ê×ÏÈÀ´¿´¿´ÔìDexFile¶ÔÏóµÄ¹¹·½·¨¡£
public final class DexFile { private int mCookie; private final String mFileName; ... public DexFile(File file) throws IOException { this(file.getPath()); } public DexFile(String fileName) throws IOException { mCookie = openDexFile(fileName, null, 0); mFileName = fileName; guard.open("close"); } private DexFile(String sourceName, String outputName, int flags) throws IOException { mCookie = openDexFile(sourceName, outputName, flags); mFileName = sourceName; guard.open("close"); } static public DexFile loadDex(String sourcePathName, String outputPathName, int flags) throws IOException { return new DexFile(sourcePathName, outputPathName, flags); } public Class loadClass(String name, ClassLoader loader) { String slashName = name.replace('.', '/'); return loadClassBinaryName(slashName, loader); } public Class loadClassBinaryName(String name, ClassLoader loader) { return defineClass(name, loader, mCookie); } private native static Class defineClass(String name, ClassLoader loader, int cookie); native private static int openDexFile(String sourceName, String outputName, int flags) throws IOException; native private static int openDexFile(byte[] fileContents) ... } |
ͨ¹ýÒÔǰ·ÖÎö¹ýµÄÔ´Â룬ÎÒÃÇÖªµÀClassLoaderÖ÷ÒªÊÇͨ¹ýDexFile.loadDexÕâ¸ö¾²Ì¬·½·¨À´´´½¨ËüÐèÒªµÄDexFileʵÀýµÄ£¬ÕâÀï´´½¨DexFileµÄʱºò£¬±£´æÁËDexÎļþµÄÎļþ·¾¶mFileName£¬Í¬Ê±µ÷ÓÃÁËopenDexFileµÄNative·½·¨´ò¿ªDexÎļþ²¢·µ»ØÁËÒ»¸ömCookieµÄÕûÐͱäÁ¿(ÎÒ²»ÖªµÀÕâ¸ö¸ÉɶÓõģ¬ÎÒ²ÂËüÊÇÒ»¸öC++ÓõÄ×ÊÔ´¾ä±ú£¬ÓÃÓÚNative²ã·ÃÎʾßÌåµÄDexÎļþ)¡£ÔÚNative²ãµÄopenDexFile·½·¨ÀÖ÷Òª×öÁ˼ì²éµ±Ç°´´½¨À´µÄDexÎļþÊÇ·ñÊÇÓÐЧµÄDexÎļþ£¬»¹ÊÇÊÇÒ»¸ö´øÓÐDexÎļþµÄѹËõ°ü£¬»¹ÊÇÒ»¸öÎÞЧµÄDexÎļþ¡£
¼ÓÔØDexÎļþÀïµÄÀà
¼ÓÔØÀàµÄʱºò£¬ClassLoaderÓÖÊÇͨ¹ýDexFile#loadClassÕâ¸ö·½·¨À´Íê³ÉµÄ£¬Õâ¸ö·½·¨Àïµ÷ÓÃÁËdefineClassÕâ¸öNative·½·¨£¬¿´À´DexFile²ÅÊǼÓÔØClassµÄ¾ßÌåAPI£¬¼ÓÔØDexÎļþºÍ¼ÓÔØ¾ßÌåClass¶¼ÊÇͨ¹ýNative·½·¨Íê³É£¬ClassLoaderÓеãÃû²»¸±Êµ°¡¡£
MultiDexµÄ¹¤×÷»úÖÆ µ±Ò»¸öDexÎļþÌ«·ÊµÄʱºò(·½·¨ÊýĿ̫¶à¡¢ÎļþÌ«´ó)£¬ÔÚ´ò°üApkÎļþµÄʱºò¾Í»á³öÎÊÌ⣬¾ÍËã´ò°üµÄʱºò²»³öÎÊÌ⣬ÔÚAndroid
5.0ÒÔÏÂÉ豸Éϰ²×°»òÔËÐÐApkÒ²»á³öÎÊÌâ(¾ßÌåÔÒò¿ÉÒԲο¼¸ø App ÆôÓà MultiDex ¹¦ÄÜ)¡£¼ÈȻһ¸öDexÎļþ²»Ðеϰ£¬ÄǾͰÑÕâ¸ö˶´óµÄDexÎļþ²ð·Ö³ÉÈô¸É¸öСµÄDexÎļþ£¬¸ÕºÃÒ»¸öClassLoader¿ÉÒÔÓжà¸öDexFile£¬Õâ¾ÍÊÇMultiDexµÄ»ù±¾Éè¼ÆË¼Â·¡£
¹¤×÷Á÷³Ì
MultiDexµÄ¹¤×÷Á÷³Ì¾ßÌå·ÖΪÁ½¸ö²¿·Ö£¬Ò»¸ö²¿·ÖÊÇ´ò°ü¹¹½¨ApkµÄʱºò£¬½«DexÎļþ²ð·Ö³ÉÈô¸É¸öСµÄDexÎļþ£¬Õâ¸öAndroid
StudioÒѾ°ïÎÒÃÇ×öÁË(ÉèÖà ¡°multiDexEnabled true¡±)£¬ÁíÒ»²¿·Ö¾ÍÊÇÔÚÆô¶¯ApkµÄʱºò£¬Í¬Ê±¼ÓÔØ¶à¸öDexÎļþ(¾ßÌåÊǼÓÔØDexÎļþÓÅ»¯ºóµÄOdexÎļþ£¬²»¹ýÎļþÃû»¹ÊÇ.dex)£¬ÕâÒ»²¿·Ö¹¤×÷´ÓAndroid
5.0¿ªÊ¼ÏµÍ³ÒѾ°ïÎÒÃÇ×öÁË£¬µ«ÊÇÔÚAndroid 5.0ÒÔǰ»¹ÊÇÐèҪͨ¹ýMultiDex Support¿âÀ´Ö§³Ö(MultiDex.install(Context))¡£
ËùÒÔÎÒÃÇÐèÒª¹ØÐĵÄÊǵڶþ²¿·Ö£¬Õâ¸ö¹ý³ÌµÄ¼òµ¥Ê¾ÒâÁ÷³ÌͼÈçÏ¡£

(ͼÖкìÉ«²¿·ÖΪºÄʱ±È½Ï´óµÄµØ·½) Ô´Âë·ÖÎö ÏÖÔÚ¹Ù·½ÒѾ²¿ÊðµÄMultiDex Support°æ±¾ÊÇcom.android.support:multidex:1.0.1£¬µ«ÊÇÏÖÔÚ²Ö¿âµÄmaster·ÖÖ§ÒѾÓÐÁËÐí¶àеÄÌá½»(ÆäÖÐ×îÃ÷ÏÔµÄÇø±ðÊǼÓÈëÁËFileLockÀ´¿ØÖÆ¶à½ø³Ìͬ²½ÎÊÌâ)£¬ËùÒÔÕâÀï·ÖÎöµÄÔ´Âë¶¼ÊÇ×îеÄmaster·ÖÖ§Éϵġ£
MultiDex SupportµÄÈë¿ÚÊÇMultiDex.install(Context)£¬ÏÈ´ÓÕâÀïÈëÊÖ°É¡£(Õâ´ÎÎҰѾßÌåµÄ·ÖÎö¶¼Ð´ÔÚ´úÂëµÄ×¢ÊÍÁË£¬ÕâÑù¿´ÊDz»ÊǸü¼ò½àÃ÷ÁËЩ?)
public static void install(Context context) { Log.i(TAG, "install"); // 1. ÅжÁÊÇ·ñÐèÒªÖ´ÐÐMultiDex¡£ if (IS_VM_MULTIDEX_CAPABLE) { Log.i(TAG, "VM has multidex support, MultiDex support library is disabled."); return; } if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) { throw new RuntimeException("Multi dex installation failed. SDK " + Build.VERSION.SDK_INT + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + "."); } try { ApplicationInfo applicationInfo = getApplicationInfo(context); if (applicationInfo == null) { // Looks like running on a test Context, so just return without patching. return; } // 2. Èç¹ûÕâ¸ö·½·¨ÒѾµ÷ÓùýÒ»´Î£¬¾Í²»ÄÜÔÙµ÷ÓÃÁË¡£ synchronized (installedApk) { String apkPath = applicationInfo.sourceDir; if (installedApk.contains(apkPath)) { return; } installedApk.add(apkPath); // 3. Èç¹ûµ±Ç°Android°æ±¾ÒѾ×ÔÉíÖ§³ÖÁËMultiDex£¬ÒÀÈ»¿ÉÒÔÖ´ÐÐMultiDex²Ù×÷£¬ // µ«ÊÇ»áÓо¯¸æ¡£ if (Build.VERSION.SDK_INT > MAX_SUPPORTED_SDK_VERSION) { Log.w(TAG, "MultiDex is not guaranteed to work in SDK version " + Build.VERSION.SDK_INT + ": SDK version higher than " + MAX_SUPPORTED_SDK_VERSION + " should be backed by " + "runtime with built-in multidex capabilty but it's not the " + "case here: java.vm.version=\"" + System.getProperty("java.vm.version") + "\""); } // 4. »ñÈ¡µ±Ç°µÄClassLoaderʵÀý£¬ºóÃæÒª×öµÄ¹¤×÷£¬¾ÍÊÇ°ÑÆäËûdexÎļþ¼ÓÔØºó£¬ // °ÑÆäDexFile¶ÔÏóÌí¼Óµ½Õâ¸öClassLoaderʵÀýÀï¾ÍÍêÊÂÁË¡£ ClassLoader loader; try { loader = context.getClassLoader(); } catch (RuntimeException e) { Log.w(TAG, "Failure while trying to obtain Context class loader. " + "Must be running in test mode. Skip patching.", e); return; } if (loader == null) { Log.e(TAG, "Context class loader is null. Must be running in test mode. " + "Skip patching."); return; } try { // 5. Çå³ý¾ÉµÄdexÎļþ£¬×¢ÒâÕâÀï²»ÊÇÇå³ýÉϴμÓÔØµÄdexÎļþ»º´æ¡£ // »ñÈ¡dex»º´æÄ¿Â¼ÊÇ£¬»áÓÅÏÈ»ñÈ¡/data/data/<package>/code-cache×÷Ϊ»º´æÄ¿Â¼¡£ // Èç¹û»ñȡʧ°Ü£¬ÔòʹÓÃ/data/data/<package>/files/code-cacheĿ¼¡£ // ÕâÀïÇå³ýµÄÊǵڶþ¸öĿ¼¡£ clearOldDexDir(context); } catch (Throwable t) { Log.w(TAG, "Something went wrong when trying to clear old MultiDex extraction, " + "continuing without cleaning.", t); } // 6. »ñÈ¡»º´æÄ¿Â¼£¨/data/data/<package>/code-cache£©¡£ File dexDir = getDexDir(context, applicationInfo); // 7. ¼ÓÔØ»º´æÎļþ£¨Èç¹ûÓУ©¡£ List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, false); // 8. ¼ì²é»º´æµÄdexÊÇ·ñ°²È« if (checkValidZipFiles(files)) { // 9. °²×°»º´æµÄdex installSecondaryDexes(loader, dexDir, files); } else { // 9. ´ÓapkѹËõ°üÀïÃæÌáÈ¡dexÎļþ Log.w(TAG, "Files were not valid zip files. Forcing a reload."); files = MultiDexExtractor.load(context, applicationInfo, dexDir, true); if (checkValidZipFiles(files)) { // 10. °²×°ÌáÈ¡µÄdex installSecondaryDexes(loader, dexDir, files); } else { throw new RuntimeException("Zip files were not valid."); } } } } catch (Exception e) { Log.e(TAG, "Multidex installation failure", e); throw new RuntimeException("Multi dex installation failed (" + e.getMessage() + ")."); } Log.i(TAG, "install done"); } |
¾ßÌå´úÂëµÄ·ÖÎöÒѾÔÚÉÏÃæ´úÂëµÄ×¢ÊÍÀï¸ø³öÁË£¬´ÓÕâÀïÎÒÃÇÒ²¿ÉÒÔ¿´³ö£¬Õû¸öMultiDex.install(Context)µÄ¹ý³ÌÖУ¬¹Ø¼üµÄ²½Öè¾ÍÊÇMultiDexExtractor#load·½·¨ºÍMultiDex#installSecondaryDexes·½·¨¡£
(Õⲿ·ÖÊÇÌâÍâ»°)ÆäÖÐÓиöMultiDex#clearOldDexDir(Context)·½·¨£¬Õâ¸ö·½·¨µÄ×÷ÓÃÊÇɾ³ý/data/data/<package>/files/code-cache£¬Ò»¿ªÊ¼ÎÒÒÔΪÕâ¸ö·½·¨ÊÇɾ³ýÉÏÒ»´ÎÖ´ÐÐMultiDexºóµÄ»º´æÎļþ£¬²»¹ýÕâÃ÷ÏÔ²»¶Ô£¬²»¿ÉÄÜÿ´ÎMultiDex¶¼ÖØÐ½âѹdexÎļþÒ»±ß£¬ÕâÑùÿ´ÎÆô¶¯»áºÜºÄʱ£¬Ö»ÓеÚÒ»´ÎÀäÆô¶¯µÄʱºò²ÅÐèÒª½âѹdexÎļþ¡£ºóÀ´ÎÒÓÖÏëÊDz»ÊÇÒÔǰ¾É°æµÄMultiDexÔø¾°Ñ»º´æÎļþ·ÅÔÚÕâ¸öĿ¼ÀÏÖÔÚа汾ֻÊÇÇå³ýÒÔǰ¾É°æµÄÒÅÁôÎļþ?µ«ÊÇÎÒÕÒ±éÁËÕû¸öMultiDex
RepoµÄÌύҲûÓмû¹ýÀàËÆµÄ¾É°æ±¾´úÂë¡£ºóÃæÎÒ×Ðϸ¿´MultiDex#getDexDirÕâ¸ö·½·¨²Å·¢ÏÖ£¬ÔÀ´MultiDexÔÚ»ñÈ¡dex»º´æÄ¿Â¼ÊÇ£¬»áÓÅÏÈ»ñÈ¡/data/data/<package>/code-cache×÷Ϊ»º´æÄ¿Â¼£¬Èç¹û»ñȡʧ°Ü£¬ÔòʹÓÃ/data/data/<package>/files/code-cacheĿ¼£¬¶øºóÕߵĻº´æÎļþ»áÔÚÿ´ÎAppÖØÐÂÆô¶¯µÄʱºò±»Çå³ý¡£¸Ð¾õMultiDex»ñÈ¡»º´æÄ¿Â¼µÄÂß¼²»ÊǺÜÑϽ÷£¬¶ø»ñÈ¡»º´æÄ¿Â¼Ê§°ÜÒ²ÊÇMultiDex¹¤×÷¹¤³ÌÖÐÉÙÊýÓÐÖØÊÔ»úÖÆµÄµØ·½£¬¿´À´MultiDexÕæµÄÊÇÒ»¸öÁÙʱµÄ¼æÈÝ·½°¸£¬GoogleÒ²Ðí²¢²»´òËãÈÏÕæ´¦ÀíÕâЩÀúÊ·µÄºÚ¹ø¡£
½ÓÏÂÀ´ÔÙ¿´¿´MultiDexExtractor#loadÕâ¸ö·½·¨¡£
static List<File> load(Context context, ApplicationInfo applicationInfo, File dexDir, boolean forceReload) throws IOException { Log.i(TAG, "MultiDexExtractor.load(" + applicationInfo.sourceDir + ", " + forceReload + ")"); final File sourceApk = new File(applicationInfo.sourceDir); // 1. »ñÈ¡µ±Ç°ApkÎļþµÄcrcÖµ¡£ long currentCrc = getZipCrc(sourceApk); // Validity check and extraction must be done only while the lock file has been taken. File lockFile = new File(dexDir, LOCK_FILENAME); RandomAccessFile lockRaf = new RandomAccessFile(lockFile, "rw"); FileChannel lockChannel = null; FileLock cacheLock = null; List<File> files; IOException releaseLockException = null; try { lockChannel = lockRaf.getChannel(); Log.i(TAG, "Blocking on lock " + lockFile.getPath()); // 2. ¼ÓÉÏÎļþËø£¬·ÀÖ¹¶à½ø³Ì³åÍ»¡£ cacheLock = lockChannel.lock(); Log.i(TAG, lockFile.getPath() + " locked"); // 3. ÏÈÅжÏÊÇ·ñÇ¿ÖÆÖØÐ½âѹ£¬ÕâÀïµÚÒ»´Î»áÓÅÏÈʹÓÃÒѽâѹ¹ýµÄdexÎļþ£¬Èç¹û¼ÓÔØÊ§°Ü¾ÍÇ¿ÖÆÖØÐ½âѹ¡£ // ´ËÍ⣬ͨ¹ýcrcºÍÎļþÐÞ¸Äʱ¼ä£¬ÅжÏÈç¹ûApkÎļþÒѾ±»Ð޸썏²¸Ç°²×°£©£¬¾Í»áÌø¹ý»º´æÖØÐ½âѹdexÎļþ¡£ if (!forceReload && !isModified(context, sourceApk, currentCrc)) { try { // 4. ¼ÓÔØ»º´æµÄdexÎļþ files = loadExistingExtractions(context, sourceApk, dexDir); } catch (IOException ioe) { Log.w(TAG, "Failed to reload existing extracted secondary dex files," + " falling back to fresh extraction", ioe); // 5. ¼ÓÔØÊ§°ÜµÄ»°ÖØÐ½âѹ£¬²¢±£´æ½âѹ³öÀ´µÄdexÎļþµÄÐÅÏ¢¡£ files = performExtractions(sourceApk, dexDir); putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1); } } else { // 4. ÖØÐ½âѹ£¬²¢±£´æ½âѹ³öÀ´µÄdexÎļþµÄÐÅÏ¢¡£ Log.i(TAG, "Detected that extraction must be performed."); files = performExtractions(sourceApk, dexDir); putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1); } } finally { if (cacheLock != null) { try { cacheLock.release(); } catch (IOException e) { Log.e(TAG, "Failed to release lock on " + lockFile.getPath()); // Exception while releasing the lock is bad, we want to report it, but not at // the price of overriding any already pending exception. releaseLockException = e; } } if (lockChannel != null) { closeQuietly(lockChannel); } closeQuietly(lockRaf); } if (releaseLockException != null) { throw releaseLockException; } Log.i(TAG, "load found " + files.size() + " secondary dex files"); return files; } |
Õâ¸ö¹ý³ÌÖ÷ÒªÊÇ»ñÈ¡¿ÉÒÔ°²×°µÄdexÎļþÁÐ±í£¬¿ÉÒÔÊÇÉϴνâѹ³öÀ´µÄ»º´æÎļþ£¬Ò²¿ÉÒÔÊÇÖØÐ´ÓApk°üÀïÃæÌáÈ¡³öÀ´µÄ¡£ÐèҪעÒâµÄʱ£¬Èç¹ûÊÇÖØÐ½âѹ£¬ÕâÀï»áÓÐÃ÷ÏԵĺÄʱ£¬¶øÇÒ½âѹ³öÀ´µÄdexÎļþ£¬»á±»Ñ¹Ëõ³É.zipѹËõ°ü£¬Ñ¹ËõµÄ¹ý³ÌÒ²»áÓÐÃ÷ÏԵĺÄʱ(ÕâÀïѹËõdexÎļþ¿ÉÄÜÊÇÎÊÁ˽ÚÊ¡¿Õ¼ä)¡£
Èç¹ûdexÎļþÊÇÖØÐ½âѹ³öÀ´µÄ£¬Ôò»á±£´ædexÎļþµÄÐÅÏ¢£¬°üÀ¨½âѹµÄapkÎļþµÄcrcÖµ¡¢ÐÞ¸Äʱ¼äÒÔ¼°dexÎļþµÄÊýÄ¿£¬ÒÔ±ãÏÂÒ»´ÎÆô¶¯Ö±½ÓʹÓÃÒѾ½âѹ¹ýµÄdex»º´æÎļþ£¬¶ø²»ÊÇÿһ´Î¶¼ÖØÐ½âѹ¡£
ÐèÒªÌØ±ðÌáµ½µÄÊÇ£¬ÀïÃæµÄFileLockÊÇ×îеÄmaster·ÖÖ§ÀïÃæÐ¼ӽøÈ¥µÄ¹¦ÄÜ£¬ÏÖÔÚ×îеÄ1.0.1°æ±¾ÀïÃæÊÇûÓеġ£
ÎÞÂÛÊÇͨ¹ýʹÓûº´æµÄdexÎļþ£¬»¹ÊÇÖØÐ´ÓapkÖнâѹdexÎļþ£¬»ñÈ¡dexÎļþÁбíºó£¬ÏÂÒ»²½¾ÍÊǰ²×°(»òÕß˵¼ÓÔØ)ÕâЩdexÎļþÁË¡£×îºóµÄ¹¤×÷ÔÚMultiDex#installSecondaryDexesÕâ¸ö·½·¨ÀïÃæ¡£
private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<File> files) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException { if (!files.isEmpty()) { if (Build.VERSION.SDK_INT >= 19) { V19.install(loader, files, dexDir); } else if (Build.VERSION.SDK_INT >= 14) { V14.install(loader, files, dexDir); } else { V4.install(loader, files); } } } |
ÒòΪÔÚ²»Í¬µÄSDK°æ±¾ÉÏ£¬ClassLoader(¸ü׼ȷÀ´ËµÊÇDexClassLoader)¼ÓÔØdexÎļþµÄ·½Ê½ÓÐËù²»Í¬£¬ËùÒÔÕâÀï×öÁËV4/V14/V19µÄ¼æÈÝ(Magic
Code)¡£
Build.VERSION.SDK_INT < 14
/** * Installer for platform versions 4 to 13. */ private static final class V4 { private static void install(ClassLoader loader, List<File> additionalClassPathEntries) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, IOException { int extraSize = additionalClassPathEntries.size(); Field pathField = findField(loader, "path"); StringBuilder path = new StringBuilder((String) pathField.get(loader)); String[] extraPaths = new String[extraSize]; File[] extraFiles = new File[extraSize]; ZipFile[] extraZips = new ZipFile[extraSize]; DexFile[] extraDexs = new DexFile[extraSize]; for (ListIterator<File> iterator = additionalClassPathEntries.listIterator(); iterator.hasNext();) { File additionalEntry = iterator.next(); String entryPath = additionalEntry.getAbsolutePath(); path.append(':').append(entryPath); int index = iterator.previousIndex(); extraPaths[index] = entryPath; extraFiles[index] = additionalEntry; extraZips[index] = new ZipFile(additionalEntry); extraDexs[index] = DexFile.loadDex(entryPath, entryPath + ".dex", 0); } // Õâ¸ö°æ±¾ÊÇ×î¼òµ¥µÄ¡£ // Ö»ÐèÒª´´½¨DexFile¶ÔÏóºó£¬Ê¹Ó÷´ÉäµÄ·½·¨·Ö±ðÀ©Õ¹ClassLoaderʵÀýµÄÒÔÏÂ×ֶμ´¿É¡£ pathField.set(loader, path.toString()); expandFieldArray(loader, "mPaths", extraPaths); expandFieldArray(loader, "mFiles", extraFiles); expandFieldArray(loader, "mZips", extraZips); expandFieldArray(loader, "mDexs", extraDexs); } } |
14 <= Build.VERSION.SDK_INT < 19
/** * Installer for platform versions 14, 15, 16, 17 and 18. */ private static final class V14 { private static void install(ClassLoader loader, List<File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException { // À©Õ¹ClassLoaderʵÀýµÄ"pathList"×ֶΡ£ Field pathListField = findField(loader, "pathList"); Object dexPathList = pathListField.get(loader); expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList<File>(additionalClassPathEntries), optimizedDirectory)); } private static Object[] makeDexElements( Object dexPathList, ArrayList<File> files, File optimizedDirectory) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Method makeDexElements = findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class); return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory); } } |
´ÓAPI14¿ªÊ¼£¬DexClassLoader»áʹÓÃÒ»¸öDexpDexPathListÀàÀ´·â×°DexFileÊý×é¡£
final class DexPathList { private static final String DEX_SUFFIX = ".dex"; private static final String JAR_SUFFIX = ".jar"; private static final String ZIP_SUFFIX = ".zip"; private static final String APK_SUFFIX = ".apk"; private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory) { ArrayList<Element> elements = new ArrayList<Element>(); for (File file : files) { ZipFile zip = null; DexFile dex = null; String name = file.getName(); if (name.endsWith(DEX_SUFFIX)) { // Raw dex file (not inside a zip/jar). try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException ex) { System.logE("Unable to load dex file: " + file, ex); } } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) || name.endsWith(ZIP_SUFFIX)) { try { zip = new ZipFile(file); } catch (IOException ex) { System.logE("Unable to open zip file: " + file, ex); } try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException ignored) { } } else { System.logW("Unknown file type for: " + file); } if ((zip != null) || (dex != null)) { elements.add(new Element(file, zip, dex)); } } return elements.toArray(new Element[elements.size()]); } private static DexFile loadDexFile(File file, File optimizedDirectory) throws IOException { if (optimizedDirectory == null) { return new DexFile(file); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0); } } } |
ͨ¹ýµ÷ÓÃDexPathList#makeDexElements·½·¨£¬¿ÉÒÔ¼ÓÔØÎÒÃÇÉÏÃæ½âѹµÃµ½µÄdexÎļþ£¬´Ó´úÂëÒ²¿ÉÒÔ¿´³ö£¬DexPathList#makeDexElementsÆäʵҲÊÇͨ¹ýµ÷ÓÃDexFile#loadDexÀ´¼ÓÔØdexÎļþ²¢´´½¨DexFile¶ÔÏóµÄ¡£V14ÖУ¬Í¨¹ý·´Éäµ÷ÓÃDexPathList#makeDexElements·½·¨¼ÓÔØÎÒÃÇÐèÒªµÄdexÎļþ£¬ÔڰѼÓÔØµÃµ½µÄÊý×éÀ©Õ¹µ½ClassLoaderʵÀýµÄ"pathList"×ֶΣ¬´Ó¶øÍê³ÉdexÎļþµÄ°²×°
´ÓDexPathListµÄ´úÂëÖÐÎÒÃÇÒ²¿ÉÒÔ¿´³ö£¬ClassLoaderÊÇÖ§³ÖÖ±½Ó¼ÓÔØ.dex/.zip/.jar/.apkµÄdexÎļþ°üµÄ(ÎҼǵÃÒÔǰÔÚÄÄÆªÈÕÖ¾ÖкÃÏñÌáµ½¹ýÀàËÆµÄÎÊÌâ¡)¡£
19 <= Build.VERSION.SDK_INT
/** * Installer for platform versions 19. */ private static final class V19 { private static void install(ClassLoader loader, List<File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException { Field pathListField = findField(loader, "pathList"); Object dexPathList = pathListField.get(loader); ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>(); expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList<File>(additionalClassPathEntries), optimizedDirectory, suppressedExceptions)); if (suppressedExceptions.size() > 0) { for (IOException e : suppressedExceptions) { Log.w(TAG, "Exception in makeDexElement", e); } Field suppressedExceptionsField = findField(dexPathList, "dexElementsSuppressedExceptions"); IOException[] dexElementsSuppressedExceptions = (IOException[]) suppressedExceptionsField.get(dexPathList); if (dexElementsSuppressedExceptions == null) { dexElementsSuppressedExceptions = suppressedExceptions.toArray( new IOException[suppressedExceptions.size()]); } else { IOException[] combined = new IOException[suppressedExceptions.size() + dexElementsSuppressedExceptions.length]; suppressedExceptions.toArray(combined); System.arraycopy(dexElementsSuppressedExceptions, 0, combined, suppressedExceptions.size(), dexElementsSuppressedExceptions.length); dexElementsSuppressedExceptions = combined; } suppressedExceptionsField.set(dexPathList, dexElementsSuppressedExceptions); } } private static Object[] makeDexElements( Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Method makeDexElements = findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class, ArrayList.class); return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory, suppressedExceptions); } } |
V19ÓëV14²î±ð²»´ó£¬Ö»²»¹ýDexPathList#makeDexElements·½·¨¶àÁËÒ»¸öArrayList<IOException>²ÎÊý£¬Èç¹ûÔÚÖ´ÐÐDexPathList#makeDexElements·½·¨µÄ¹ý³ÌÖгöÏÖÒì³££¬ºóÃæÊ¹Ó÷´ÉäµÄ·½Ê½°ÑÕâЩÒì³£¼Ç¼½øDexPathListµÄdexElementsSuppressedExceptions×Ö¶ÎÀïÃæ¡£
ÎÞÂÛÊÇV4/V14»¹ÊÇV19£¬ÔÚ´´½¨DexFile¶ÔÏóµÄʱºò£¬¶¼ÐèҪͨ¹ýDexFileµÄNative·½·¨openDexFileÀ´´ò¿ªdexÎļþ£¬Æä¾ßÌåϸ½ÚÔݲ»ÌÖÂÛ(Éæ¼°µ½dexµÄÎļþ½á¹¹£¬ºÜ·³£¬ÓÐÐËȤÇëÔĶÁdalvik_system_DexFile.cpp)£¬Õâ¸ö¹ý³ÌµÄÖ÷ҪĿµÄÊǸøµ±Ç°µÄdexÎļþ×öOptimizeÓÅ»¯´¦Àí²¢Éú³ÉÏàͬÎļþÃûµÄodexÎļþ£¬Appʵ¼Ê¼ÓÔØÀàµÄʱºò£¬¶¼ÊÇͨ¹ýodexÎļþ½øÐеġ£ÒòΪÿ¸öÉ豸¶Ôodex¸ñʽµÄÒªÇó¶¼²»Ò»Ñù£¬ËùÒÔÕâ¸öÓÅ»¯µÄ²Ù×÷Ö»ÄÜ·ÅÔÚ°²×°ApkµÄʱºò´¦Àí£¬Ö÷dexµÄÓÅ»¯ÎÒÃÇÒѾÔÚ°²×°apkµÄʱºò¸ã¶¨ÁË£¬ÆäÓàµÄdex¾ÍÊÇÔÚMultiDex#installSecondaryDexesÀïÃæÓÅ»¯µÄ£¬¶øºóÕßÒ²ÊÇMultiDex¹ý³ÌÖУ¬ÁíÍâÒ»¸öºÄʱ±È½Ï¶àµÄ²Ù×÷¡£(ÔÚMultiDexÖУ¬ÌáÈ¡³öÀ´µÄdexÎļþ±»Ñ¹Ëõ³É.zipÎļþ£¬ÓÖÓÅ»¯ºóµÄodexÎļþÔò±»±£´æÎª.dexÎļþ¡£)
µ½ÕâÀMultiDexµÄ¹¤×÷Á÷³Ì¾Í½áÊøÁË¡£ÔõôÑù£¬ÊDz»ÊǾõµÃºÍÒÔǰ̸µ½¶¯Ì¬¼ÓÔØ¼¼Êõ(²å¼þ»¯)µÄʱºò˵µÄºÜÏñ?û´í£¬Ë½ÐËüÃǵĺËÐͼÊÇdexÎļþÄØ¡£JavaÀÏʦµÚÒ»½Ú¿Î¾Í˵¡°Àà¾ÍÊDZà³Ì¡±£¬¸ã¶¨ÀàÄã¾ÍÄܸ㶨Õû¸öÊÀ½ç°¡!
ÓÅ»¯·½°¸
MultiDexÓиö±È½Ïµ°ÌÛµÄÎÊÌ⣬¾ÍÊÇ»á²úÉúÃ÷ÏԵĿ¨¶ÙÏÖÏó£¬Í¨¹ýÉÏÃæµÄ·ÖÎö£¬ÎÒÃÇÖªµÀ¾ßÌåµÄ¿¨¶Ù²úÉúÔÚ½âѹdexÎļþÒÔ¼°ÓÅ»¯dexÁ½¸ö²½Öè¡£²»¹ýºÃÔÚ£¬ÔÚApplication#attachBaseContext(Context)ÖУ¬UIÏ̵߳Ä×èÈûÊDz»»áÒý·¢ANRµÄ£¬Ö»²»¹ýÕâ¶Î³¤Ê±¼äµÄ¿¨¶Ù(°×ÆÁ)»¹ÊÇ»áÓ°ÏìÓû§ÌåÑé¡£
Ŀǰ£¬ÓÅ»¯·½°¸ÄÜÏëµ½µÄÓÐÁ½ÖÖ¡£
PreMultiDex·½°¸
´óÖÂ˼·ÊÇ£¬ÔÚ°²×°Ò»¸öеÄapkµÄʱºò£¬ÏÈÔÚWorkerÏß³ÌÀï×öºÃMultiDexµÄ½âѹºÍOptimize¹¤×÷£¬°²×°apk²¢Æô¶¯ºó£¬Ö±½ÓʹÓÃ֮ǰOptimize²úÉúµÄodexÎļþ£¬ÕâÑù¾Í¿ÉÒÔ±ÜÃâµÚÒ»´ÎÆô¶¯Ê±ºòµÄOptimize¹¤×÷¡£

°²×°dexµÄʱºò£¬ºËÐÄÊÇ´´½¨DexFile¶ÔÏó²¢Ê¹ÓÃÆäNative·½·¨¶ÔdexÎļþ½øÐÐopt´¦Àí£¬Í¬Ê±Éú²úÒ»¸öÓëdexÎļþ(.zip)ͬÃûµÄÒѾopt¹ýµÄdexÎļþ(.dex)¡£Èç¹û°²×°dexµÄʱºò£¬Õâ¸öopt¹ýµÄdexÎļþÒѾ´æÔÚ£¬ÔòÌø¹ýÕâ¸ö¹ý³Ì£¬Õâ»á½ÚÊ¡Ðí¶àºÄʱ¡£ËùÒÔÓÅ»¯µÄ˼·¾ÍÊÇ£¬ÏÂÔØApkÍê³ÉµÄʱºò£¬Ô¤ÏȽâѹdexÎļþ£¬²¢Ô¤ÏÈ´¥·¢°²×°dexÎļþÒÔÉú²úopt¹ýµÄdexÎļþ¡£ÕâÑù¸²¸Ç°²×°Apk²¢Æô¶¯µÄʱºò£¬Èç¹ûMultiDexÄÜÃüÖнâѹºÃµÄdexºÍodexÎļþ£¬ÔòÄܱܿªºÄʱ×î´óµÄÁ½¸ö²Ù×÷¡£
²»¹ýÕâ¸ö·½°¸µÄȱµãÒ²ÊÇÃ÷ÏԵ쬵ÚÒ»´Î°²×°µÄapkûÓÐ×÷Ó㬶øÇÒÊÂÏÈÐèҪʹÓÃÄÚÖõÄapk¸üй¦ÄܰÑа汾µÄapkÎļþÏÂÔØÏÂÀ´ºó£¬²ÅÄÜ×öPreMultiDex¹¤×÷¡£
Òì²½MultiDex·½°¸
ÕâÖÖ·½°¸Ò²ÊÇĿǰ±È½ÏÁ÷ÐеÄDexÊÖ¶¯·Ö°ü·½°¸£¬Æô¶¯AppµÄʱºò£¬ÏÈÏÔʾһ¸ö¼òµ¥µÄSplashÉÁÆÁ½çÃæ£¬È»ºóÆô¶¯WorkerÏß³ÌÖ´ÐÐMultiDex#install(Context)¹¤×÷£¬¾Í¿ÉÒÔ±ÜÃâUIÏß³Ì×èÈû¡£²»¹ýҪȷ±£Æô¶¯ÒÔ¼°Æô¶¯MultiDex#install(Context)ËùÐèÒªµÄÀà¶¼ÔÚÖ÷dexÀïÃæ(ÊÖ¶¯·Ö°ü)£¬¶øÇÒÐèÒª´¦ÀíºÃ½ø³Ìͬ²½ÎÊÌâ¡£ |