上一篇我们讲了一下multide加载的机制与原理,但或许你在自定义加载的时候还是会遇到一些特别的异常问题,今天一个童鞋和我说了一个自定义的小bug,我帮他分析并提供了解决思路,这里大概分享一下。
其实问题也不是很复杂,异常为无法反射到DexPathList类的makeDexElements方法异常,很多人会问,为什么mutildex自己反射的时候没这个问题,我们自定义就会出现呢?这个问题其实很难回答,只能说碰到那个坑填那个,很多涉及到权限,内核逻辑代码的知识,我们一时也没法说清,所以解决眼前问题,并根据问题去理清问题的来源与相关知识才是一条大道,当然这个问题的原因我们是知道的 ( VM has multidex support, MultiDex support library is disabled ) 。
但我们仍然想multidex加载怎么办,那我们就绕过这个门槛,第一点注释掉这行代码
代码如下:
if (IS_VM_MULTIDEX_CAPABLE) {
Log.i(TAG, "VM has multidex support, MultiDex support library is disabled.");
return;
}
绕过这个阻碍,multidex在load多dex的时候就到了我们上面的问题,方法无法反射,那我们去DexPathList这个类看一下,它到底做了些什么事情,代码如下:
@SuppressLint("NewApi") /*package*/ @TargetApi(Build.VERSION_CODES.GINGERBREAD) final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
/** class definition context */
private final ClassLoader definingContext;
/**
* List of dex/resource (class path) elements.
* Should be called pathElements, but the Facebook app uses reflection
* to modify 'dexElements' (http://b/7726934).
*/
private Element[] dexElements;
/** List of native library path elements. */
private final NativeLibraryElement[] nativeLibraryPathElements;
/** List of application native library directories. */
private final List<File> nativeLibraryDirectories;
/** List of system native library directories. */
private final List<File> systemNativeLibraryDirectories;
/**
* Exceptions thrown during creation of the dexElements list.
*/
private IOException[] dexElementsSuppressedExceptions;
/**
* Construct an instance.
*
* @param definingContext the context in which any as-yet unresolved
* classes should be defined
*
* @param dexFiles the bytebuffers containing the dex files that we should load classes from.
*/
public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) {
if (definingContext == null) {
throw new NullPointerException("definingContext == null");
}
if (dexFiles == null) {
throw new NullPointerException("dexFiles == null");
}
Stream<ByteBuffer> obs = Arrays.stream(dexFiles);
if (obs == null) {
throw new NullPointerException("dexFiles contains a null Buffer!");
}
this.definingContext = definingContext;
// TODO It might be useful to let in-memory dex-paths have native libraries.
this.nativeLibraryDirectories = Collections.emptyList();
this.systemNativeLibraryDirectories =
splitPaths(System.getProperty("java.library.path"), true);
this.nativeLibraryPathElements = makePathElements(this.systemNativeLibraryDirectories);
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
if (suppressedExceptions.size() > 0) {
this.dexElementsSuppressedExceptions =
suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
} else {
dexElementsSuppressedExceptions = null;
}
}
/**
* Constructs an instance.
*
* @param definingContext the context in which any as-yet unresolved
* classes should be defined
* @param dexPath list of dex/resource path elements, separated by
* {@code File.pathSeparator}
* @param librarySearchPath list of native library directory path elements,
* separated by {@code File.pathSeparator}
* @param optimizedDirectory directory where optimized {@code .dex} files
* should be found and written to, or {@code null} to use the default
* system directory for same
*/
public DexPathList(ClassLoader definingContext, String dexPath,
String librarySearchPath, File optimizedDirectory) {
if (definingContext == null) {
throw new NullPointerException("definingContext == null");
}
if (dexPath == null) {
throw new NullPointerException("dexPath == null");
}
if (optimizedDirectory != null) {
if (!optimizedDirectory.exists()) {
throw new IllegalArgumentException(
"optimizedDirectory doesn't exist: "
+ optimizedDirectory);
}
if (!(optimizedDirectory.canRead()
&& optimizedDirectory.canWrite())) {
throw new IllegalArgumentException(
"optimizedDirectory not readable/writable: "
+ optimizedDirectory);
}
}
this.definingContext = definingContext;
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
// save dexPath for BaseDexClassLoader
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
suppressedExceptions, definingContext);
// Native libraries may exist in both the system and
// application library paths, and we use this search order:
//
// 1. This class loader's library path for application libraries (librarySearchPath):
// 1.1. Native library directories
// 1.2. Path to libraries in apk-files
// 2. The VM's library path from the system property for system libraries
// also known as java.library.path
//
// This order was reversed prior to Gingerbread; see http://b/2933456.
this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
this.systemNativeLibraryDirectories =
splitPaths(System.getProperty("java.library.path"), true);
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
if (suppressedExceptions.size() > 0) {
this.dexElementsSuppressedExceptions =
suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
} else {
dexElementsSuppressedExceptions = null;
}
}
@Override public String toString() {
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
File[] nativeLibraryDirectoriesArray =
allNativeLibraryDirectories.toArray(
new File[allNativeLibraryDirectories.size()]);
return "DexPathList[" + Arrays.toString(dexElements) +
",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectoriesArray) + "]";
}
/**
* For BaseDexClassLoader.getLdLibraryPath.
*/
public List<File> getNativeLibraryDirectories() {
&n