自定义multidex热加载无法反射DexPathList方法的解决方案

当自定义multidex加载时,可能会遇到无法反射DexPathList的makeDexElements方法的异常。问题源于VM已具备多DEX支持,导致多DEX库被禁用。为解决此问题,可以注释掉检查VM是否支持multidex的代码,然后通过DexClassLoader绕过该限制。文章还提供了具体代码实现和解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇我们讲了一下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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值