Android 使用android-support-multidex解决Dex超出方法数的限制问题

        随着应用不断迭代,业务线的扩展,应用越来越大(比如集成了各种第三方sdk或者公共支持的jar包,项目耦合性高,重复作用的类越来越多),相信很多人都遇到过如下的错误:

UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536
at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)
at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)
at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
at com.android.dx.command.dexer.Main.run(Main.java:230)
at com.android.dx.command.dexer.Main.main(Main.java:199)
at com.android.dx.command.Main.main(Main.java:103)

        没错,你的应用中的Dex 文件方法数超过了最大值65536的上限,简单来说,应用爆棚了.

        那么让我们看一下为什么会引起这种错误:

        在Android系统中,一个App的所有代码都在一个Dex文件里面。Dex是一个类似Jar的存储了多有Java编译字节码的归档文件。因为Android系统使用Dalvik虚拟机,所以需要把使用Java Compiler编译之后的class文件转换成Dalvik能够执行的class文件。这里需要强调的是,DexJar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOptDexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。但是在早期的Android系统中,DexOpt有一个问题,也就是这篇文章想要说明并解决的问题。DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。尽管在新版本的Android系统中,DexOpt修复了这个问题,但是我们仍然需要对低版本的Android系统做兼容.


那么问题来了怎么做兼容呢?我自己的实践是这样的:

1、把一些jar包做成classesX.dex(这里的X代表一个整数从2开始例如:classes2.dex、classes3.dex),把做成的dex文件放到src目录下。这里的命名和存放路径都是由android-support-multidex.jar这个包规定的,我本人也是经过反编译后读程序了解到的。

2、在自己的项目下新建lib目录把需要的jar包放在里边然后在Order and Export里边手动选择jar包,不要勾选已经制作成dex的jar就行了,这一步应该了解Order and Export的作用是什么(解释一下吧作用就是勾选的jar会打进你的应用程序里边,不勾选当然就不会打进去了,这个可以自己反编译apk文件看看就清楚了)。

3、让项目里边原来继承Application的类继承MultiDexApplication就可以了。至于为啥这样做看了android-support-multidex.jar里边的源码就很清楚了,当然了java基础要过关哦。

下面是android-support-multidex.jar的源码:

//MultiDexApplication.java
package android.support.multidex;

import android.app.Application;
import android.content.Context;

// Referenced classes of package android.support.multidex:
//			MultiDex

public class MultiDexApplication extends Application
{

	public MultiDexApplication()
	{
	}

	protected void attachBaseContext(Context base)
	{
		super.attachBaseContext(base);
		MultiDex.install(this);
	}
}

主要是下面MultiDexExtractor这个类可以好好读读会发现很多惊喜的。

//MultiDexExtractor.java
package android.support.multidex;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.util.Log;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.*;

// Referenced classes of package android.support.multidex:
//			ZipUtil

final class MultiDexExtractor
{

	private static final String TAG = "MultiDex";
	private static final String DEX_PREFIX = "classes";
	private static final String DEX_SUFFIX = ".dex";
	private static final String EXTRACTED_NAME_EXT = ".classes";
	private static final String EXTRACTED_SUFFIX = ".zip";
	private static final int MAX_EXTRACT_ATTEMPTS = 3;
	private static final String PREFS_FILE = "multidex.version";
	private static final String KEY_TIME_STAMP = "timestamp";
	private static final String KEY_CRC = "crc";
	private static final String KEY_DEX_NUMBER = "dex.number";
	private static final int BUFFER_SIZE = 16384;
	private static final long NO_VALUE = -1L;
	private static Method sApplyMethod;

	MultiDexExtractor()
	{
	}

	static List load(Context context, ApplicationInfo applicationInfo, File dexDir, boolean forceReload)
		throws IOException
	{
		Log.i("MultiDex", (new StringBuilder()).append("MultiDexExtractor.load(").append(applicationInfo.sourceDir).append(", ").append(forceReload).append(")").toString());
		File sourceApk = new File(applicationInfo.sourceDir);
		long currentCrc = getZipCrc(sourceApk);
		List files;
		if (!forceReload && !isModified(context, sourceApk, currentCrc))
		{
			try
			{
				files = loadExistingExtractions(context, sourceApk, dexDir);
			}
			catch (IOException ioe)
			{
				Log.w("MultiDex", "Failed to reload existing extracted secondary dex files, falling back to fresh extraction", ioe);
				files = performExtractions(sourceApk, dexDir);
				putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1);
			}
		} else
		{
			Log.i("MultiDex", "Detected that extraction must be performed.");
			files = performExtractions(sourceApk, dexDir);
			putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1);
		}
		Log.i("MultiDex", (new StringBuilder()).append("load found ").append(files.size()).append(" secondary dex files").toString());
		return files;
	}

	private static List loadExistingExtractions(Context context, File sourceApk, File dexDir)
		throws IOException
	{
		Log.i("MultiDex", "loading existing secondary dex files");
		String extractedFilePrefix = (new StringBuilder()).append(sourceApk.getName()).append(".classes").toString();
		int totalDexNumber = getMultiDexPreferences(context).getInt("dex.number", 1);
		List files = new ArrayList(totalDexNumber);
		for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++)
		{
			String fileName = (new StringBuilder()).append(extractedFilePrefix).append(secondaryNumber).append(".zip").toString();
			File extractedFile = new File(dexDir, fileName);
			if (extractedFile.isFile())
			{
				files.add(extractedFile);
				if (!verifyZipFile(extractedFile))
				{
					Log.i("MultiDex", (new StringBuilder()).append("Invalid zip file: ").append(extractedFile).toString());
					throw new IOException("Invalid ZIP file.");
				}
			} else
			{
				throw new IOException((new StringBuilder()).append("Missing extracted secondary dex file '").append(extractedFile.getPath()).append("'").toString());
			}
		}

		return files;
	}

	private static boolean isModified(Context context, File archive, long currentCrc)
	{
		SharedPreferences prefs = getMultiDexPreferences(context);
		return prefs.getLong("timestamp", -1L) != getTimeStamp(archive) || prefs.getLong("crc", -1L) != currentCrc;
	}

	private static long getTimeStamp(File archive)
	{
		long timeStamp = archive.lastModified();
		if (timeStamp == -1L)
			timeStamp--;
		return timeStamp;
	}

	private static long getZipCrc(File archive)
		throws IOException
	{
		long computedValue = ZipUtil.getZipCrc(archive);
		if (computedValue == -1L)
			computedValue--;
		return computedValue;
	}

	private static List performExtractions(File sourceApk, File dexDir)
		throws IOException
	{
		String extractedFilePrefix;
		List files;
		ZipFile apk;
		extractedFilePrefix = (new StringBuilder()).append(sourceApk.getName()).append(".classes").toString();
		prepareDexDir(dexDir, extractedFilePrefix);
		files = new ArrayList();
		apk = new ZipFile(sourceApk);
		int secondaryNumber = 2;
		for (ZipEntry dexFile = apk.getEntry((new StringBuilder()).append("classes").append(secondaryNumber).append(".dex").toString()); dexFile != null; dexFile = apk.getEntry((new StringBuilder()).append("classes").append(secondaryNumber).append(".dex").toString()))
		{
			String fileName = (new StringBuilder()).append(extractedFilePrefix).append(secondaryNumber).append(".zip").toString();
			File extractedFile = new File(dexDir, fileName);
			files.add(extractedFile);
			Log.i("MultiDex", (new StringBuilder()).append("Extraction is needed for file ").append(extractedFile).toString());
			int numAttempts = 0;
			boolean isExtractionSuccessful = false;
			do
			{
				if (numAttempts >= 3 || isExtractionSuccessful)
					break;
				numAttempts++;
				extract(apk, dexFile, extractedFile, extractedFilePrefix);
				isExtractionSuccessful = verifyZipFile(extractedFile);
				Log.i("MultiDex", (new StringBuilder()).append("Extraction ").append(isExtractionSuccessful ? "success" : "failed").append(" - length ").append(extractedFile.getAbsolutePath()).append(": ").append(extractedFile.length()).toString());
				if (!isExtractionSuccessful)
				{
					extractedFile.delete();
					if (extractedFile.exists())
						Log.w("MultiDex", (new StringBuilder()).append("Failed to delete corrupted secondary dex '").append(extractedFile.getPath()).append("'").toString());
				}
			} while (true);
			if (!isExtractionSuccessful)
				throw new IOException((new StringBuilder()).append("Could not create zip file ").append(extractedFile.getAbsolutePath()).append(" for secondary dex (").append(secondaryNumber).append(")").toString());
			secondaryNumber++;
		}

		try
		{
			apk.close();
		}
		catch (IOException e)
		{
			Log.w("MultiDex", "Failed to close resource", e);
		}
		break MISSING_BLOCK_LABEL_451;
		Exception exception;
		exception;
		try
		{
			apk.close();
		}
		catch (IOException e)
		{
			Log.w("MultiDex", "Failed to close resource", e);
		}
		throw exception;
		return files;
	}

	private static void putStoredApkInfo(Context context, long timeStamp, long crc, int totalDexNumber)
	{
		SharedPreferences prefs = getMultiDexPreferences(context);
		android.content.SharedPreferences.Editor edit = prefs.edit();
		edit.putLong("timestamp", timeStamp);
		edit.putLong("crc", crc);
		edit.putInt("dex.number", totalDexNumber);
		apply(edit);
	}

	private static SharedPreferences getMultiDexPreferences(Context context)
	{
		return context.getSharedPreferences("multidex.version", android.os.Build.VERSION.SDK_INT >= 11 ? 4 : 0);
	}

	private static void prepareDexDir(File dexDir, String extractedFilePrefix)
		throws IOException
	{
		File cache = dexDir.getParentFile();
		mkdirChecked(cache);
		mkdirChecked(dexDir);
		FileFilter filter = new FileFilter(extractedFilePrefix) {

			final String val$extractedFilePrefix;

			public boolean accept(File pathname)
			{
				return !pathname.getName().startsWith(extractedFilePrefix);
			}

			
			{
				extractedFilePrefix = s;
				super();
			}
		};
		File files[] = dexDir.listFiles(filter);
		if (files == null)
		{
			Log.w("MultiDex", (new StringBuilder()).append("Failed to list secondary dex dir content (").append(dexDir.getPath()).append(").").toString());
			return;
		}
		File arr$[] = files;
		int len$ = arr$.length;
		for (int i$ = 0; i$ < len$; i$++)
		{
			File oldFile = arr$[i$];
			Log.i("MultiDex", (new StringBuilder()).append("Trying to delete old file ").append(oldFile.getPath()).append(" of size ").append(oldFile.length()).toString());
			if (!oldFile.delete())
				Log.w("MultiDex", (new StringBuilder()).append("Failed to delete old file ").append(oldFile.getPath()).toString());
			else
				Log.i("MultiDex", (new StringBuilder()).append("Deleted old file ").append(oldFile.getPath()).toString());
		}

	}

	private static void mkdirChecked(File dir)
		throws IOException
	{
		dir.mkdir();
		if (!dir.isDirectory())
		{
			File parent = dir.getParentFile();
			if (parent == null)
				Log.e("MultiDex", (new StringBuilder()).append("Failed to create dir ").append(dir.getPath()).append(". Parent file is null.").toString());
			else
				Log.e("MultiDex", (new StringBuilder()).append("Failed to create dir ").append(dir.getPath()).append(". parent file is a dir ").append(parent.isDirectory()).append(", a file ").append(parent.isFile()).append(", exists ").append(parent.exists()).append(", readable ").append(parent.canRead()).append(", writable ").append(parent.canWrite()).toString());
			throw new IOException((new StringBuilder()).append("Failed to create cache directory ").append(dir.getPath()).toString());
		} else
		{
			return;
		}
	}

	private static void extract(ZipFile apk, ZipEntry dexFile, File extractTo, String extractedFilePrefix)
		throws IOException, FileNotFoundException
	{
		InputStream in;
		ZipOutputStream out;
		File tmp;
		in = apk.getInputStream(dexFile);
		out = null;
		tmp = File.createTempFile(extractedFilePrefix, ".zip", extractTo.getParentFile());
		Log.i("MultiDex", (new StringBuilder()).append("Extracting ").append(tmp.getPath()).toString());
		out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tmp)));
		ZipEntry classesDex = new ZipEntry("classes.dex");
		classesDex.setTime(dexFile.getTime());
		out.putNextEntry(classesDex);
		byte buffer[] = new byte[16384];
		for (int length = in.read(buffer); length != -1; length = in.read(buffer))
			out.write(buffer, 0, length);

		out.closeEntry();
		out.close();
		break MISSING_BLOCK_LABEL_170;
		Exception exception;
		exception;
		out.close();
		throw exception;
		Log.i("MultiDex", (new StringBuilder()).append("Renaming to ").append(extractTo.getPath()).toString());
		if (!tmp.renameTo(extractTo))
			throw new IOException((new StringBuilder()).append("Failed to rename \"").append(tmp.getAbsolutePath()).append("\" to \"").append(extractTo.getAbsolutePath()).append("\"").toString());
		closeQuietly(in);
		tmp.delete();
		break MISSING_BLOCK_LABEL_285;
		Exception exception1;
		exception1;
		closeQuietly(in);
		tmp.delete();
		throw exception1;
	}

	static boolean verifyZipFile(File file)
	{
		ZipFile zipFile = new ZipFile(file);
		zipFile.close();
		return true;
		IOException e;
		e;
		Log.w("MultiDex", (new StringBuilder()).append("Failed to close zip file: ").append(file.getAbsolutePath()).toString());
		break MISSING_BLOCK_LABEL_115;
		ZipException ex;
		ex;
		Log.w("MultiDex", (new StringBuilder()).append("File ").append(file.getAbsolutePath()).append(" is not a valid zip file.").toString(), ex);
		break MISSING_BLOCK_LABEL_115;
		ex;
		Log.w("MultiDex", (new StringBuilder()).append("Got an IOException trying to open zip file: ").append(file.getAbsolutePath()).toString(), ex);
		return false;
	}

	private static void closeQuietly(Closeable closeable)
	{
		try
		{
			closeable.close();
		}
		catch (IOException e)
		{
			Log.w("MultiDex", "Failed to close resource", e);
		}
	}

	private static void apply(android.content.SharedPreferences.Editor editor)
	{
		if (sApplyMethod != null)
			try
			{
				sApplyMethod.invoke(editor, new Object[0]);
				return;
			}
			catch (InvocationTargetException unused) { }
			catch (IllegalAccessException unused) { }
		editor.commit();
	}

	static 
	{
		try
		{
			Class cls = android/content/SharedPreferences$Editor;
			sApplyMethod = cls.getMethod("apply", new Class[0]);
		}
		catch (NoSuchMethodException unused)
		{
			sApplyMethod = null;
		}
	}
}

//MultiDex.java
package android.support.multidex;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import dalvik.system.DexFile;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;

// Referenced classes of package android.support.multidex:
//			MultiDexExtractor

public final class MultiDex
{
	private static final class V4
	{

		private static void install(ClassLoader loader, List additionalClassPathEntries)
			throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, IOException
		{
			int extraSize = additionalClassPathEntries.size();
			Field pathField = MultiDex.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 iterator = additionalClassPathEntries.listIterator(); iterator.hasNext();)
			{
				File additionalEntry = (File)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, (new StringBuilder()).append(entryPath).append(".dex").toString(), 0);
			}

			pathField.set(loader, path.toString());
			MultiDex.expandFieldArray(loader, "mPaths", extraPaths);
			MultiDex.expandFieldArray(loader, "mFiles", extraFiles);
			MultiDex.expandFieldArray(loader, "mZips", extraZips);
			MultiDex.expandFieldArray(loader, "mDexs", extraDexs);
		}


		private V4()
		{
		}
	}

	private static final class V14
	{

		private static void install(ClassLoader loader, List additionalClassPathEntries, File optimizedDirectory)
			throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException
		{
			Field pathListField = MultiDex.findField(loader, "pathList");
			Object dexPathList = pathListField.get(loader);
			MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory));
		}

		private static Object[] makeDexElements(Object dexPathList, ArrayList files, File optimizedDirectory)
			throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
		{
			Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", new Class[] {
				java/util/ArrayList, java/io/File
			});
			return (Object[])(Object[])makeDexElements.invoke(dexPathList, new Object[] {
				files, optimizedDirectory
			});
		}


		private V14()
		{
		}
	}

	private static final class V19
	{

		private static void install(ClassLoader loader, List additionalClassPathEntries, File optimizedDirectory)
			throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException
		{
			Field pathListField = MultiDex.findField(loader, "pathList");
			Object dexPathList = pathListField.get(loader);
			ArrayList suppressedExceptions = new ArrayList();
			MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions));
			if (suppressedExceptions.size() > 0)
			{
				IOException e;
				for (Iterator i$ = suppressedExceptions.iterator(); i$.hasNext(); Log.w("MultiDex", "Exception in makeDexElement", e))
					e = (IOException)i$.next();

				Field suppressedExceptionsField = MultiDex.findField(loader, "dexElementsSuppressedExceptions");
				IOException dexElementsSuppressedExceptions[] = (IOException[])(IOException[])suppressedExceptionsField.get(loader);
				if (dexElementsSuppressedExceptions == null)
				{
					dexElementsSuppressedExceptions = (IOException[])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(loader, dexElementsSuppressedExceptions);
			}
		}

		private static Object[] makeDexElements(Object dexPathList, ArrayList files, File optimizedDirectory, ArrayList suppressedExceptions)
			throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
		{
			Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", new Class[] {
				java/util/ArrayList, java/io/File, java/util/ArrayList
			});
			return (Object[])(Object[])makeDexElements.invoke(dexPathList, new Object[] {
				files, optimizedDirectory, suppressedExceptions
			});
		}


		private V19()
		{
		}
	}


	static final String TAG = "MultiDex";
	private static final String OLD_SECONDARY_FOLDER_NAME = "secondary-dexes";
	private static final String SECONDARY_FOLDER_NAME;
	private static final int MAX_SUPPORTED_SDK_VERSION = 20;
	private static final int MIN_SDK_VERSION = 4;
	private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
	private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
	private static final Set installedApk = new HashSet();
	private static final boolean IS_VM_MULTIDEX_CAPABLE = isVMMultidexCapable(System.getProperty("java.vm.version"));

	private MultiDex()
	{
	}

	public static void install(Context context)
	{
		Log.i("MultiDex", "install");
		if (IS_VM_MULTIDEX_CAPABLE)
		{
			Log.i("MultiDex", "VM has multidex support, MultiDex support library is disabled.");
			return;
		}
		if (android.os.Build.VERSION.SDK_INT < 4)
			throw new RuntimeException((new StringBuilder()).append("Multi dex installation failed. SDK ").append(android.os.Build.VERSION.SDK_INT).append(" is unsupported. Min SDK version is ").append(4).append(".").toString());
		ApplicationInfo applicationInfo;
		String apkPath;
		ClassLoader loader;
		RuntimeException e;
		Throwable t;
		File dexDir;
		List files;
		try
		{
			applicationInfo = getApplicationInfo(context);
			if (applicationInfo == null)
				return;
		}
		catch (Exception e)
		{
			Log.e("MultiDex", "Multidex installation failure", e);
			throw new RuntimeException((new StringBuilder()).append("Multi dex installation failed (").append(e.getMessage()).append(").").toString());
		}
label0:
		{
			synchronized (installedApk)
			{
				apkPath = applicationInfo.sourceDir;
				if (!installedApk.contains(apkPath))
					break label0;
			}
			return;
		}
		installedApk.add(apkPath);
		if (android.os.Build.VERSION.SDK_INT > 20)
			Log.w("MultiDex", (new StringBuilder()).append("MultiDex is not guaranteed to work in SDK version ").append(android.os.Build.VERSION.SDK_INT).append(": SDK version higher than ").append(20).append(" should be backed by ").append("runtime with built-in multidex capabilty but it's not the ").append("case here: java.vm.version=\"").append(System.getProperty("java.vm.version")).append("\"").toString());
		try
		{
			loader = context.getClassLoader();
			break MISSING_BLOCK_LABEL_216;
		}
		// Misplaced declaration of an exception variable
		catch (RuntimeException e)
		{
			Log.w("MultiDex", "Failure while trying to obtain Context class loader. Must be running in test mode. Skip patching.", e);
		}
		set;
		JVM INSTR monitorexit ;
		return;
		if (loader != null)
			break MISSING_BLOCK_LABEL_232;
		Log.e("MultiDex", "Context class loader is null. Must be running in test mode. Skip patching.");
		set;
		JVM INSTR monitorexit ;
		return;
		try
		{
			clearOldDexDir(context);
		}
		// Misplaced declaration of an exception variable
		catch (Throwable t)
		{
			Log.w("MultiDex", "Something went wrong when trying to clear old MultiDex extraction, continuing without cleaning.", t);
		}
		dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
		files = MultiDexExtractor.load(context, applicationInfo, dexDir, false);
		if (checkValidZipFiles(files))
		{
			installSecondaryDexes(loader, dexDir, files);
		} else
		{
			Log.w("MultiDex", "Files were not valid zip files.  Forcing a reload.");
			files = MultiDexExtractor.load(context, applicationInfo, dexDir, true);
			if (checkValidZipFiles(files))
				installSecondaryDexes(loader, dexDir, files);
			else
				throw new RuntimeException("Zip files were not valid.");
		}
		set;
		JVM INSTR monitorexit ;
		  goto _L1
		exception;
		throw exception;
_L1:
		Log.i("MultiDex", "install done");
		return;
	}

	private static ApplicationInfo getApplicationInfo(Context context)
		throws android.content.pm.PackageManager.NameNotFoundException
	{
		PackageManager pm;
		String packageName;
		try
		{
			pm = context.getPackageManager();
			packageName = context.getPackageName();
		}
		catch (RuntimeException e)
		{
			Log.w("MultiDex", "Failure while trying to obtain ApplicationInfo from Context. Must be running in test mode. Skip patching.", e);
			return null;
		}
		if (pm == null || packageName == null)
		{
			return null;
		} else
		{
			ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 128);
			return applicationInfo;
		}
	}

	static boolean isVMMultidexCapable(String versionString)
	{
		boolean isMultidexCapable = false;
		if (versionString != null)
		{
			Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
			if (matcher.matches())
				try
				{
					int major = Integer.parseInt(matcher.group(1));
					int minor = Integer.parseInt(matcher.group(2));
					isMultidexCapable = major > 2 || major == 2 && minor >= 1;
				}
				catch (NumberFormatException e) { }
		}
		Log.i("MultiDex", (new StringBuilder()).append("VM with version ").append(versionString).append(isMultidexCapable ? " has multidex support" : " does not have multidex support").toString());
		return isMultidexCapable;
	}

	private static void installSecondaryDexes(ClassLoader loader, File dexDir, List files)
		throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException
	{
		if (!files.isEmpty())
			if (android.os.Build.VERSION.SDK_INT >= 19)
				V19.install(loader, files, dexDir);
			else
			if (android.os.Build.VERSION.SDK_INT >= 14)
				V14.install(loader, files, dexDir);
			else
				V4.install(loader, files);
	}

	private static boolean checkValidZipFiles(List files)
	{
		for (Iterator i$ = files.iterator(); i$.hasNext();)
		{
			File file = (File)i$.next();
			if (!MultiDexExtractor.verifyZipFile(file))
				return false;
		}

		return true;
	}

	private static Field findField(Object instance, String name)
		throws NoSuchFieldException
	{
		Class clazz = instance.getClass();
_L2:
		if (clazz == null)
			break; /* Loop/switch isn't completed */
		Field field;
		field = clazz.getDeclaredField(name);
		if (!field.isAccessible())
			field.setAccessible(true);
		return field;
		NoSuchFieldException e;
		e;
		clazz = clazz.getSuperclass();
		if (true) goto _L2; else goto _L1
_L1:
		throw new NoSuchFieldException((new StringBuilder()).append("Field ").append(name).append(" not found in ").append(instance.getClass()).toString());
	}

	private static transient Method findMethod(Object instance, String name, Class parameterTypes[])
		throws NoSuchMethodException
	{
		Class clazz = instance.getClass();
_L2:
		if (clazz == null)
			break; /* Loop/switch isn't completed */
		Method method;
		method = clazz.getDeclaredMethod(name, parameterTypes);
		if (!method.isAccessible())
			method.setAccessible(true);
		return method;
		NoSuchMethodException e;
		e;
		clazz = clazz.getSuperclass();
		if (true) goto _L2; else goto _L1
_L1:
		throw new NoSuchMethodException((new StringBuilder()).append("Method ").append(name).append(" with parameters ").append(Arrays.asList(parameterTypes)).append(" not found in ").append(instance.getClass()).toString());
	}

	private static void expandFieldArray(Object instance, String fieldName, Object extraElements[])
		throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException
	{
		Field jlrField = findField(instance, fieldName);
		Object original[] = (Object[])(Object[])jlrField.get(instance);
		Object combined[] = (Object[])(Object[])Array.newInstance(((Object) (original)).getClass().getComponentType(), original.length + extraElements.length);
		System.arraycopy(((Object) (original)), 0, ((Object) (combined)), 0, original.length);
		System.arraycopy(((Object) (extraElements)), 0, ((Object) (combined)), original.length, extraElements.length);
		jlrField.set(instance, ((Object) (combined)));
	}

	private static void clearOldDexDir(Context context)
		throws Exception
	{
		File dexDir = new File(context.getFilesDir(), "secondary-dexes");
		if (dexDir.isDirectory())
		{
			Log.i("MultiDex", (new StringBuilder()).append("Clearing old secondary dex dir (").append(dexDir.getPath()).append(").").toString());
			File files[] = dexDir.listFiles();
			if (files == null)
			{
				Log.w("MultiDex", (new StringBuilder()).append("Failed to list secondary dex dir content (").append(dexDir.getPath()).append(").").toString());
				return;
			}
			File arr$[] = files;
			int len$ = arr$.length;
			for (int i$ = 0; i$ < len$; i$++)
			{
				File oldFile = arr$[i$];
				Log.i("MultiDex", (new StringBuilder()).append("Trying to delete old file ").append(oldFile.getPath()).append(" of size ").append(oldFile.length()).toString());
				if (!oldFile.delete())
					Log.w("MultiDex", (new StringBuilder()).append("Failed to delete old file ").append(oldFile.getPath()).toString());
				else
					Log.i("MultiDex", (new StringBuilder()).append("Deleted old file ").append(oldFile.getPath()).toString());
			}

			if (!dexDir.delete())
				Log.w("MultiDex", (new StringBuilder()).append("Failed to delete secondary dex dir ").append(dexDir.getPath()).toString());
			else
				Log.i("MultiDex", (new StringBuilder()).append("Deleted old secondary dex dir ").append(dexDir.getPath()).toString());
		}
	}

	static 
	{
		SECONDARY_FOLDER_NAME = (new StringBuilder()).append("code_cache").append(File.separator).append("secondary-dexes").toString();
	}



}

//ZipUtil.java
package android.support.multidex;

import java.io.*;
import java.util.zip.CRC32;
import java.util.zip.ZipException;

final class ZipUtil
{
	static class CentralDirectory
	{

		long offset;
		long size;

		CentralDirectory()
		{
		}
	}


	private static final int ENDHDR = 22;
	private static final int ENDSIG = 0x6054b50;
	private static final int BUFFER_SIZE = 16384;

	ZipUtil()
	{
	}

	static long getZipCrc(File apk)
		throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile(apk, "r");
		long l;
		CentralDirectory dir = findCentralDirectory(raf);
		l = computeCrcOfCentralDir(raf, dir);
		raf.close();
		return l;
		Exception exception;
		exception;
		raf.close();
		throw exception;
	}

	static CentralDirectory findCentralDirectory(RandomAccessFile raf)
		throws IOException, ZipException
	{
		long scanOffset = raf.length() - 22L;
		if (scanOffset < 0L)
			throw new ZipException((new StringBuilder()).append("File too short to be a zip file: ").append(raf.length()).toString());
		long stopOffset = scanOffset - 0x10000L;
		if (stopOffset < 0L)
			stopOffset = 0L;
		int endSig = Integer.reverseBytes(0x6054b50);
		do
		{
			raf.seek(scanOffset);
			if (raf.readInt() != endSig)
			{
				scanOffset--;
				if (scanOffset < stopOffset)
					throw new ZipException("End Of Central Directory signature not found");
			} else
			{
				raf.skipBytes(2);
				raf.skipBytes(2);
				raf.skipBytes(2);
				raf.skipBytes(2);
				CentralDirectory dir = new CentralDirectory();
				dir.size = (long)Integer.reverseBytes(raf.readInt()) & 0xffffffffL;
				dir.offset = (long)Integer.reverseBytes(raf.readInt()) & 0xffffffffL;
				return dir;
			}
		} while (true);
	}

	static long computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir)
		throws IOException
	{
		CRC32 crc = new CRC32();
		long stillToRead = dir.size;
		raf.seek(dir.offset);
		int length = (int)Math.min(16384L, stillToRead);
		byte buffer[] = new byte[16384];
		length = raf.read(buffer, 0, length);
		do
		{
			if (length == -1)
				break;
			crc.update(buffer, 0, length);
			stillToRead -= length;
			if (stillToRead == 0L)
				break;
			length = (int)Math.min(16384L, stillToRead);
			length = raf.read(buffer, 0, length);
		} while (true);
		return crc.getValue();
	}
}

总共就四个类文件,至于dex的制作和需要主要的问题相信网上会有很多。自己的实践在这里做个简单的比较希望大神们莫笑。




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值