Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
MultiDex¹¤×÷Ô­Àí·ÖÎöºÍÓÅ»¯·½°¸
 
×÷Õß Kaede À´Ô´£º51CTO ·¢²¼ÓÚ£º 2016-12-20
  2157  次浏览      27
 

¶¯Ì¬¼ÓÔØ¼¼Êõ(²å¼þ»¯)ϵÁÐÒѾ­¿ÓÁËÓÐÒ»¶Îʱ¼äÁË£¬²»¹ý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ÀïÃæ(ÊÖ¶¯·Ö°ü)£¬¶øÇÒÐèÒª´¦ÀíºÃ½ø³Ìͬ²½ÎÊÌâ¡£

   
2157 ´Îä¯ÀÀ       27
 
Ïà¹ØÎÄÕÂ

ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖÓë̽ÌÖ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
 
Ïà¹ØÎĵµ

Android_UI¹Ù·½Éè¼Æ½Ì³Ì
ÊÖ»ú¿ª·¢Æ½Ì¨½éÉÜ
androidÅÄÕÕ¼°ÉÏ´«¹¦ÄÜ
Android½²ÒåÖÇÄÜÊÖ»ú¿ª·¢
Ïà¹Ø¿Î³Ì

Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
Androidϵͳ¿ª·¢
AndroidÓ¦Óÿª·¢
ÊÖ»úÈí¼þ²âÊÔ
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

androidÈË»ú½çÃæÖ¸ÄÏ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
AndroidÊÖ»ú¿ª·¢£¨¶þ£©
AndroidÊÖ»ú¿ª·¢£¨Èý£©
AndroidÊÖ»ú¿ª·¢£¨ËÄ£©
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖ̽ÌÖ
ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
ÊÖ»úÈí¼þ×Ô¶¯»¯²âÊÔÑо¿±¨¸æ


Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
AndroidÓ¦Óÿª·¢
Androidϵͳ¿ª·¢
ÊÖ»úÈí¼þ²âÊÔ
ǶÈëʽÈí¼þ²âÊÔ
AndroidÈí¡¢Ó²¡¢ÔÆÕûºÏ


ÁìÏÈIT¹«Ë¾ android¿ª·¢Æ½Ì¨×î¼Ñʵ¼ù
±±¾© Android¿ª·¢¼¼Êõ½ø½×
ijÐÂÄÜÔ´ÁìÓòÆóÒµ Android¿ª·¢¼¼Êõ
ijº½Ì칫˾ Android¡¢IOSÓ¦ÓÃÈí¼þ¿ª·¢
°¢¶û¿¨ÌØ LinuxÄÚºËÇý¶¯
°¬Ä¬Éú ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
Î÷ÃÅ×Ó Ç¶Èëʽ¼Ü¹¹Éè¼Æ