涉足这个行业多年,总想写点什么,但又不知道写什么。于是静下心想想,有什么可以总结的经验。目前做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的解决办法。^_^