涉足這個行業多年,總想寫點什麼,但又不知道寫什麼。于是靜下心想想,有什麼可以總結的經驗。目前做Android方面的應用很多人都會遇到dex 65536 的問題,雖然網上有很多這方面的解決方案,但是我感覺不是很完美,因為65536的問題不僅僅隻是method,feild也是65536的限制。那麼我先說說方法數超限的解決方法(本文的亮點在最後哦,希望對你有幫助)。其實這是一種分包方式,然後通過動态加載dex,這個的優點很明顯:不需要定義什麼接口去newInstance,工程裡該怎麼引用還是怎麼引用。但是也有一個小小要求:Application裡不能直接引用分包的dex内的class(這個有解決辦法,後面提到)。
說到它的優點請看下面兩段code:
<span style="font-size:14px;">try {
Class clazz = Class.forName("com.jejun.dex.simple.Test");
ITest instance = (ITest) clazz.newInstance();
instance.test();
} catch (Exception e) {
e.printStackTrace();
}</span>
以上的code在android裡是一般的加載方式,這個缺點很明顯,限制很多,如果是第三方jar包想弄成動态加載,必須得改寫源碼,這樣很麻煩,那麼我們要說的方式就是下面的code:
Test test = new Test();
test.test();
這段code中的Test類已經分包出去,但是寫法跟以前沒分包前沒有差別。同樣的如果要引用第三方jar包,并且把第三方jar包進行動态加載,我們的code不需要做任何改變。下面就詳細介紹下實作過程:
1.在此我用的是一個開源的分包jar(pathtool.jar,請參考:https://github.com/mmin18/Dex65536),首選我們建立一個工程,裡面寫入超過65536個方法的類,并打成jar包,再建立另一工程作為主工程,把剛打的jar放入libs目錄 下,然後在主工程根目錄 下建立一個xml檔案,檔案名随意,隻需要在build.xml裡對應上就要可以,在此我全名為custom_rules.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
<dirname property="custom_rules.basedir" file="${ant.file.custom_rules}"/>
<path id="pathtool.antlibs">
<pathelement path="${custom_rules.basedir}/pathtool.jar" />
</path>
<taskdef resource="anttasks.properties" classpathref="pathtool.antlibs" target="_blank" rel="external nofollow" />
<target name="-post-compile">
<!--
如果想要把主工程libs目錄下的所有jar都打到第二個dex裡則libs="libs/",如果需要指定某個或某些jar打入第二個dex則libs="/lockSDK_5.1.jar"或以英文逗号分隔lis="locSDK_5.1.jar,/baidumapapi_v3_4_0.jar"
-->
<pathtool
libs="/locSDK_5.1.jar,/baidumapapi_v3_4_0.jar"
refid="project.all.jars.path"
excludeRefid="out.dex.jar.input.ref"
includeRefid="out.dex.jar.assets" />
<mkdir dir="${out.absolute.dir}/libs.apk"/>
<dex executable="${dx}"
output="${out.absolute.dir}/libs.apk/classes.dex"
dexedlibs="${out.absolute.dir}/libs.apk"
nolocals="true"
forceJumbo="false"
disableDexMerger="false">
<path refid="out.dex.jar.assets" />
</dex>
<zip destfile="${out.absolute.dir}/libs-unaligned.apk"
basedir="${out.absolute.dir}/libs.apk"
includes="classes.dex"
update="true"/>
<zipalign
executable="${zipalign}"
input="${out.absolute.dir}/libs-unaligned.apk"
output="${asset.absolute.dir}/libs.apk"
verbose="${verbose}" />
</target>
<target name="-post-package">
<delete file="${asset.absolute.dir}/libs.apk"/>
</target>
<target name="run">
<xpath input="${manifest.abs.file}" expression="/manifest/@package" output="manifest.package" />
<xpath input="${manifest.abs.file}" expression="/manifest/application/activity[intent-filter/action/@android:name="android.intent.action.MAIN" and intent-filter/category/@android:name="android.intent.category.LAUNCHER"]/@android:name"
output="manifest.activity"/>
<echo>component: ${manifest.package}/${manifest.activity}</echo>
<exec executable="${adb}" failοnerrοr="false">
<arg line="${adb.device.arg}" />
<arg value="shell" />
<arg value="am" />
<arg value="force-stop" />
<arg value="${manifest.package}" />
</exec>
<exec executable="${adb}" failοnerrοr="true">
<arg line="${adb.device.arg}" />
<arg value="shell" />
<arg value="am" />
<arg value="start" />
<arg value="-n" />
<arg value="${manifest.package}/${manifest.activity}" />
<arg value="-W" />
</exec>
</target>
<target name="rund">
<xpath input="${manifest.abs.file}" expression="/manifest/@package" output="manifest.package" />
<xpath input="${manifest.abs.file}" expression="/manifest/application/activity[intent-filter/action/@android:name="android.intent.action.MAIN" and intent-filter/category/@android:name="android.intent.category.LAUNCHER"]/@android:name"
output="manifest.activity"/>
<echo>component: ${manifest.package}/${manifest.activity}</echo>
<echo>Debug package ${mainfest.package}. You should prepare your eclipse.</echo>
<echo>Keep your project open, and if you get a red bug icon in DDMS, you</echo>
<echo>should stop and manually debug it once.</echo>
<exec executable="${adb}" failοnerrοr="false">
<arg line="${adb.device.arg}" />
<arg value="shell" />
<arg value="am" />
<arg value="force-stop" />
<arg value="${manifest.package}" />
</exec>
<exec executable="${adb}" failοnerrοr="true">
<arg line="${adb.device.arg}" />
<arg value="shell" />
<arg value="am" />
<arg value="set-debug-app" />
<arg value="${manifest.package}" />
</exec>
<exec executable="${adb}" failοnerrοr="true">
<arg line="${adb.device.arg}" />
<arg value="shell" />
<arg value="am" />
<arg value="start" />
<arg value="-n" />
<arg value="${manifest.package}/${manifest.activity}" />
<arg value="-W" />
<arg value="-D" />
</exec>
</target>
<target name="help">
<!-- displays starts at col 13
|13 80| -->
<echo>Android Ant Build. Available targets:</echo>
<echo> help: Displays this help.</echo>
<echo> clean: Removes output files created by other targets.</echo>
<echo> This calls the same target on all dependent projects.</echo>
<echo> Use 'ant nodeps clean' to only clean the local project</echo>
<echo> debug: Builds the application and signs it with a debug key.</echo>
<echo> The 'nodeps' target can be used to only build the</echo>
<echo> current project and ignore the libraries using:</echo>
<echo> 'ant nodeps debug'</echo>
<echo> release: Builds the application. The generated apk file must be</echo>
<echo> signed before it is published.</echo>
<echo> The 'nodeps' target can be used to only build the</echo>
<echo> current project and ignore the libraries using:</echo>
<echo> 'ant nodeps release'</echo>
<echo> instrument:Builds an instrumented package and signs it with a</echo>
<echo> debug key.</echo>
<echo> test: Runs the tests. Project must be a test project and</echo>
<echo> must have been built. Typical usage would be:</echo>
<echo> ant [emma] debug install test</echo>
<echo> emma: Transiently enables code coverage for subsequent</echo>
<echo> targets.</echo>
<echo> install: Installs the newly build package. Must either be used</echo>
<echo> in conjunction with a build target (debug/release/</echo>
<echo> instrument) or with the proper suffix indicating</echo>
<echo> which package to install (see below).</echo>
<echo> If the application was previously installed, the</echo>
<echo> application is reinstalled if the signature matches.</echo>
<echo> installd: Installs (only) the debug package.</echo>
<echo> installr: Installs (only) the release package.</echo>
<echo> installi: Installs (only) the instrumented package.</echo>
<echo> installt: Installs (only) the test and tested packages (unless</echo>
<echo> nodeps is used as well.</echo>
<echo> uninstall: Uninstalls the application from a running emulator or</echo>
<echo> device. Also uninstall tested package if applicable</echo>
<echo> unless 'nodeps' is used as well.</echo>
<echo></echo>
<echo>Custom targets:</echo>
<echo> run: Run your application.</echo>
<echo> rund: Run and attach to debugger.</echo>
<echo></echo>
<echo>--> Example:</echo>
<echo>--> ant debug install run</echo>
<echo>--> ant rund</echo>
</target>
</project>
完後再打開build.xml确定是否有加入
<import file="custom_rules.xml" optional="true" />
如果沒有則手動添加進去,最好是在
<import file="${sdk.dir}/tools/ant/build.xml" />
這句話的前面,即:
<import file="custom_rules.xml" optional="true" />
<import file="${sdk.dir}/tools/ant/build.xml" />
一切準備工作好像完成,接下來就要用ant指令來執行我們寫的指令了。喔,忘了ant指令是需要有ant編譯環境的,ant相關的知識就不補充了。在此運作ant debug,打成一個debug包,我們剛才打成的dex在asset目錄下,在一個apk的壓縮檔案libs.apk内,當然這個打成的包你是無法調用libs.apk内dex的方法的。接下來就講講怎麼做到無逢的調用(直接new對象)
2.其實大家有去了解classloader的加載原理就會明白,android應用的dex的加載是由一個PathClassLoader加載,這個類加載器有一個父類加載器就是虛拟機的加載器(這個類加載器是加載android系統的class),那麼網上有一種做法是在應用類加載器(PathClassLoader)與虛拟機類加載器之間強行插入一個我們自己的DexClassLoader,直接上Code:
private void dexTool() {
FiledexDir = new File(getFilesDir(), "dlibs");
dexDir.mkdir();
FiledexFile = new File(dexDir, "libs.apk");
FiledexOpt = getCacheDir();
try{
InputStreamins = getAssets().open("libs.apk");
if(dexFile.length() != ins.available()) {
FileOutputStreamfos = new FileOutputStream(dexFile);
byte[]buf = new byte[4096];
intl;
while((l = ins.read(buf)) != -1) {
fos.write(buf,0, l);
}
fos.close();
}
ins.close();
}catch (Exception e) {
thrownew RuntimeException(e);
}
ClassLoadercl = getClassLoader();
ApplicationInfoai = getApplicationInfo();
StringnativeLibraryDir = null;
if(Build.VERSION.SDK_INT > 8) {
nativeLibraryDir= ai.nativeLibraryDir;
}else {
nativeLibraryDir= "/data/data/" + ai.packageName + "/lib/";
}
DexClassLoaderdcl = new DexClassLoader(dexFile.getAbsolutePath(),
dexOpt.getAbsolutePath(),nativeLibraryDir, cl.getParent());
try{
Fieldf = ClassLoader.class.getDeclaredField("parent");
f.setAccessible(true);
f.set(cl,dcl);
}catch (Exception e) {
thrownew RuntimeException(e);
}
}
但是,這種做法有一種局限性,就是分包的dex中的類不能引用到沒有分包的dex中的類或方法,這種局限性是由于類的加載機制的限制(樹加載),如圖
此圖表明,類的查找隻有一個方向,是以強行插入的類加載器裡的類如果有引用到PathClassLoader的類則會報找不到某某類(class not find exception)。為了消除這個限制,想到一個辦法,如果有看google官方提供的android-support-multidex的源碼就會知道,如下圖解釋了此種方案的類加載器結構:
下面就提供下兩個檔案的源碼
MultiDex.java
/*
* File Name:MultiDex.java
* Copyright:Copyright Jejun All Rights Reserved.
* Description:MultiDex.java
* Modify By:jejun
* Modify Date:2015-5-24
* Modify Type:Add
*/
package com.jejun.dex.simple.multidex;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;
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;
/**
*
* @author jejun
* @version jejun v.1.0 2015-6-24
* @since jejun v.1.0
*/
public class MultiDex {
static final String TAG = "MultiDex";
private static final String OLD_SECONDARY_FOLDER_NAME = "secondary-dexes";
private static final String SECONDARY_FOLDER_NAME = "code_cache"
+ File.separator + "secondary-dexes";
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<String> installedApk = new HashSet();
public static void install(Context context) {
Log.i("MultiDex", "install");
if (Build.VERSION.SDK_INT < 4) {
throw new RuntimeException("Multi dex installation failed. SDK "
+ Build.VERSION.SDK_INT
+ " is unsupported. Min SDK version is " + 4 + ".");
}
try {
ClassLoader loader;
try {
loader = context.getClassLoader();
} catch (RuntimeException e) {
Log.w("MultiDex",
"Failure while trying to obtain Context class loader. Must be running in test mode. Skip patching.",
e);
return;
}
if (loader == null) {
Log.e("MultiDex",
"Context class loader is null. Must be running in test mode. Skip patching.");
return;
}
File dexDir = MultiDexExtractor.load(context);
File[] files = dexDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return filename.endsWith(".apk");
}
});
Log.i(TAG, "loader before:" + context.getClassLoader());
installSecondaryDexes(loader, dexDir, Arrays.asList(files));
Log.i(TAG, "loader end:" + context.getClassLoader());
} catch (Exception e) {
Log.e("MultiDex", "Multidex installation failure", e);
throw new RuntimeException("Multi dex installation failed ("
+ e.getMessage() + ").");
}
Log.i("MultiDex", "install done");
}
private static ApplicationInfo getApplicationInfo(Context context)
throws PackageManager.NameNotFoundException {
String packageName;
PackageManager pm;
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;
}
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
PackageManager.GET_META_DATA);
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 localNumberFormatException) {
}
}
Log.i("MultiDex", "VM with version "
+ versionString
+ (isMultidexCapable ? " has multidex support"
: " does not have multidex support"));
return isMultidexCapable;
}
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);
}
}
private static Field findField(Object instance, String name)
throws NoSuchFieldException {
for (Class clazz = instance.getClass(); clazz != null;) {
try {
Field field = clazz.getDeclaredField(name);
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
} catch (NoSuchFieldException localNoSuchFieldException) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException("Field " + name + " not found in "
+ instance.getClass());
}
private static Method findMethod(Object instance, String name,
Class<?>[] parameterTypes) throws NoSuchMethodException {
for (Class clazz = instance.getClass(); clazz != null;) {
try {
Method method = clazz.getDeclaredMethod(name, parameterTypes);
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method;
} catch (NoSuchMethodException localNoSuchMethodException) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchMethodException("Method " + name + " with parameters "
+ Arrays.asList(parameterTypes) + " not found in "
+ instance.getClass());
}
private static void expandFieldArray(Object instance, String fieldName,
Object[] extraElements) throws NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field jlrField = findField(instance, fieldName);
Object[] original = (Object[]) jlrField.get(instance);
Object[] combined = (Object[]) Array.newInstance(original.getClass()
.getComponentType(), original.length + extraElements.length);
System.arraycopy(original, 0, combined, 0, original.length);
System.arraycopy(extraElements, 0, combined, original.length,
extraElements.length);
jlrField.set(instance, combined);
}
private static void clearOldDexDir(Context context) throws Exception {
File dexDir = new File(context.getFilesDir(), "secondary-dexes");
if (dexDir.isDirectory()) {
Log.i("MultiDex",
"Clearing old secondary dex dir (" + dexDir.getPath()
+ ").");
File[] files = dexDir.listFiles();
if (files == null) {
Log.w("MultiDex", "Failed to list secondary dex dir content ("
+ dexDir.getPath() + ").");
return;
}
for (File oldFile : files) {
Log.i("MultiDex",
"Trying to delete old file " + oldFile.getPath()
+ " of size " + oldFile.length());
if (!oldFile.delete())
Log.w("MultiDex",
"Failed to delete old file " + oldFile.getPath());
else {
Log.i("MultiDex", "Deleted old file " + oldFile.getPath());
}
}
if (!dexDir.delete())
Log.w("MultiDex", "Failed to delete secondary dex dir "
+ dexDir.getPath());
else
Log.i("MultiDex",
"Deleted old secondary dex dir " + dexDir.getPath());
}
}
private static final class V14 {
private static void install(ClassLoader loader,
List<File> 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<File> files, File optimizedDirectory)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method makeDexElements = MultiDex.findMethod(dexPathList,
"makeDexElements", new Class[] { ArrayList.class,
File.class });
return (Object[]) makeDexElements.invoke(dexPathList, new Object[] {
files, optimizedDirectory });
}
}
private static final class V19 {
private static void install(ClassLoader loader,
List<File> additionalClassPathEntries, File optimizedDirectory)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, InvocationTargetException,
NoSuchMethodException {
Field pathListField = MultiDex.findField(loader, "pathList");
Object dexPathList = pathListField.get(loader);
ArrayList<IOException> suppressedExceptions = new ArrayList();
MultiDex.expandFieldArray(
dexPathList,
"dexElements",
makeDexElements(dexPathList, new ArrayList(
additionalClassPathEntries), optimizedDirectory,
suppressedExceptions));
if (suppressedExceptions.size() > 0) {
for (IOException e : suppressedExceptions) {
Log.w("MultiDex", "Exception in makeDexElement", e);
}
Field suppressedExceptionsField = MultiDex.findField(loader,
"dexElementsSuppressedExceptions");
IOException[] dexElementsSuppressedExceptions = (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<File> files, File optimizedDirectory,
ArrayList<IOException> suppressedExceptions)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method makeDexElements = MultiDex.findMethod(dexPathList,
"makeDexElements", new Class[] { ArrayList.class,
File.class, ArrayList.class });
return (Object[]) makeDexElements.invoke(dexPathList, new Object[] {
files, optimizedDirectory, suppressedExceptions });
}
}
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 = 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];
ListIterator iterator = additionalClassPathEntries.listIterator();
while (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, entryPath
+ ".dex", 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);
}
}
}
MultiDexExtractor.java
/*
* File Name:MultiDexExtractor.java
* Copyright:Copyright 1987-2015 Jejun All Rights Reserved.
* Description: MultiDexExtractor.java
* Modify By:l00327619
* Modify Date:2015-6-24
* Modify Type:Add
*/
package com.jejun.dex.simple.multidex;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.util.Log;
final class MultiDexExtractor {
<span style="white-space:pre"> </span>@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
<span style="white-space:pre"> </span>static File load(Context context) {
<span style="white-space:pre"> </span>File dexDir = new File(context.getCacheDir(), "dlibs");
<span style="white-space:pre"> </span>if (!dexDir.exists()) {
<span style="white-space:pre"> </span>dexDir.mkdir();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>// Log.i("debug", "dex load");
<span style="white-space:pre"> </span>File dexFile = new File(dexDir, "libs.apk");
<span style="white-space:pre"> </span>boolean isExists = dexFile.exists();
<span style="white-space:pre"> </span>if (!isExists || (LepusAppConfiguration.getInstance().isProduce() && curVersionName != null && !curVersionName.equals(lastVersionName)) || LepusAppConfiguration.getInstance().isBeta() || LepusAppConfiguration.getInstance().isSit()) {
<span style="white-space:pre"> </span>InputStream ins = null;
<span style="white-space:pre"> </span>FileOutputStream fos = null;
<span style="white-space:pre"> </span>Log.i("debug", "dex file copy");
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>ins = context.getAssets().open("libs.apk");
<span style="white-space:pre"> </span>if (dexFile.length() != ins.available()) {
<span style="white-space:pre"> </span>fos = new FileOutputStream(dexFile);
<span style="white-space:pre"> </span>byte[] buf = new byte[4096];
<span style="white-space:pre"> </span>int l;
<span style="white-space:pre"> </span>while ((l = ins.read(buf)) != -1) {
<span style="white-space:pre"> </span>fos.write(buf, 0, l);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>fos.close();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>ins.close();
<span style="white-space:pre"> </span>} catch (Exception e) {
<span style="white-space:pre"> </span>// throw new RuntimeException(e);
<span style="white-space:pre"> </span>} finally {
<span style="white-space:pre"> </span>if (fos != null) {
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>fos.close();
<span style="white-space:pre"> </span>} catch (IOException e) {
<span style="white-space:pre"> </span>e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>if (ins != null) {
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>ins.close();
<span style="white-space:pre"> </span>} catch (IOException e) {
<span style="white-space:pre"> </span>// e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>return dexDir;
<span style="white-space:pre"> </span>}
}
</pre><pre>
有了這些後,你隻要在Application中重寫attachBaseContext,在方法中加入MultiDex.install(this)。如果你想在Application裡初始化一些東西,并且初始化中有引用分包中的類(如果直接在onCreate中填寫code會報找不到類的錯誤,因為你在attachBaseContext設定了自己的類加載器,但此時并沒有把分包的dex中class加載到記憶體),告訴你一個方法可以避免(有時候會很好用哦),把你要初始化的代碼用一個Handler包裹起來:
new Handler().post(new Runnable(){
@Override
public void run() {
//初始化code
}
})
後續寫上在一些情景中由于R.java中資源檔案重複導緻field超65536的解決辦法。^_^