随着应用不断迭代,业务线的扩展,应用越来越大(比如集成了各种第三方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文件。这里需要强调的是,Dex和Jar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载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的制作和需要主要的问题相信网上会有很多。自己的实践在这里做个简单的比较希望大神们莫笑。