Android动态加载插件APK

这篇博客探讨了Android应用程序遇到65k方法数限制和APK安装失败的问题,提出了通过动态加载插件APK的解决方案。文章详细介绍了如何创建插件APK,实现自定义类加载器,以及动态加载插件APK中的内容。最终,通过实例展示了如何在主Activity中显示插件APK提供的日期。
摘要由CSDN通过智能技术生成

问题起因

我曾经在开发Android Application的过程中遇到过那个有名的65k方法数的问题。如果你开发的应用程序变得非常庞大,你八成会遇到这个问题。

这个问题实际上体现为两个方面:
一、65k方法数
Android的APK安装包将编译后的字节码放在dex格式的文件中,供Android的JVM加载执行。不幸的是,单个dex文件的方法数被限制在了65536之内,这其中除了我们自己实现的方法之外,还包括了我们用到的Android Framework方法、其他library包含的方法。如果我们的方法总数超过了这个限制,那么我们在尝试打包时,会抛出如下异常:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

在比较新的Android构建工具下可能是如下异常:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

二、APK安装失败
Android官方推荐了一个叫做MultiDex的工具,用来在打包时将方法分散放到多个dex内,以此来解决65K方法数的问题。但是,除此之外,方法数过多还会带来dex文件过大的问题。

在安装APK时,系统会运行一个叫做dexopt的程序,dexopt会使用Dalvik LinearAlloc缓冲区来存储应用的方法信息。在Android 2.x的系统中,该缓冲区大小仅为5M,当我们的dex文件过大超过该缓冲区大小时,就会遇到APK安装失败的问题。

思路

对于如上的两个问题,有个非常有名的方案,就是采用动态加载插件化APK的方法。

插件化APK的思路为:将部分代码分离出来放在另外的APK中,做成插件APK的形式,在我们的应用程序启动后,在使用时动态加载该插件APK中的内容。

该思路简单来说便是将部分代码放在了另外一个独立的APK中,而不是放在我们自己的dex中。这样一方面减少了我们自己dex中方法总数,另一方面也减小了dex文件的大小,因此可以解决如上两个方面的问题。对于这个插件APK包含的类,我们可以在使用到的时候再加载进来,这便是动态加载的思路。

要实现插件化APK,我们只需要解决如下3个问题:

  • 如何生成插件APK

  • 如何加载插件APK

  • 如何使用插件APK中的内容

类加载器

在实现插件化APK之前,我们需要先了解一下Android中的类加载机制,作为实现动态加载的基础。

在Android中,我们通过ClassLoader来加载应用程序运行需要的类。ClassLoader是一个抽象类,我们需要继承该类来实现具体的类加载器的行为。在Android中,ClassLoader的实现类采用了代理模型(Delegation Model)来执行类的加载。每一个ClassLoader类都有一个与之相关联的父加载器,当一个ClassLoader类尝试加载某个类时,首先会委托其父加载器加载该类。如果父加载器成功加载了该类,则不会再由该子加载器进行加载;如果父加载器未能加载成功,则再由子加载器进行类加载的动作。

在Android中,我们一般使用DexClassLoaderPathClassLoader进行类的加载。

  • DexClassLoader: 可以从.jar或者.apk文件中加载类;

  • PathClassLoader: 只能从系统内存中已安装的内容中加载类。

对于我们的插件化APK,显然需要使用DexClassLoader进行自定义类加载。我们看一下DexClassLoader的构造方法:

/**
 * Create DexClassLoader
 * @param dexPath String: the list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android
 * @param optimizedDirectory String: directory where optimized dex files should be written; must not be null
 * @param librarySearchPath String: the list of directories containing native libraries, delimited by File.pathSeparator; may be null
 * @param parent ClassLoader: the parent class loader
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值