插件化的优势
让用户不用重新安装 APK 就能升级应用功能,减少发版本频率,增加用户体验。
提供一种快速修复线上 BUG 和更新的能力
按需加载不同的模块,实现灵活的功能配置,减少服务器对旧版本接口兼容压力
模块化、解耦合、并行开发、 65535 问题
需要掌握的知识
Binder
要实现四大组件的插件化,就需要在Binder上做修改。Binder服务端的内容没办法修改,只能改客户端的代码。学习Binder的最好方式就是AIDL。你可以读到很多关于AIDL的资料,通过制订一个aidl文件自动生成一个Java类,研究一下这个Java类的每个方法和变量,然后再反过来看四大组件,其实都是跟AIDL差不多的方式。
进程间通讯支持同步、异步、Binder、广播等。
关于Binder系列的文章:
1, 动脑学院安卓学院
3,彻底掌握 Binder(已阅)
4,Bind机制完全总结(已阅)
5,带你起源Android Binder的设计 (已阅)
Binder的优势:
而Binder机制的UID/PID是由Binder机制本身在内核空间添加身份标识,安全性高;并且Binder可以建立私有通道,这是linux的通信机制所无法实现的(Linux访问的接入点是开放的)。
App 打包的流程
执行一次打包操作,中途经历了资源打包、 Dex生成、签名等过程。其中最重要的就是资源的打包,即AAPT这一步,如果宿主和插件的资源id冲突,一种解决办法就是在这里做修改。
熟悉App的安装流程
手机安装 App 的时候,经常会有下载异常,提示资源包不能解析,这时需要知道安装 App 的这段代码在什么地方,这只是第一步。第二步需要知道, App 下载到本地后,具体要做哪些事情。手机有些目录不能访问, App 下载到本地之后,放到哪个目录下,然后会生成哪些文件。插件化有个增量更新的概念,如何下载一个增量包,从本地具体哪个位置取出一个包,这个包的具体命名规则是什么,等等。这些细节都必须要清楚明白。
App 的启动流程
Activity 启动有几种方式?一种是写一个 startActivity ,第二种是点击手机 App ,通过手机系统里的 Launcher 机制,启动 App 里默认的 Activity 。通常, App 开发人员喜闻乐见的方式是第二种。那么第一种方式的启动原理是什么呢?另外,启动的时候,Main 函数在哪里?这个 Main 函数的位置很重要,我们可以对它所在的类做修改,从而实现插件化。
插件Dex的加载
如何把插件 Dex 中的类加载到内存?另外是资源加载的问题。插件可能是 Apk 也可能是 so 格式,不管哪一种,都不会生成 R.id ,从而没办法使用。这个问题有以下几种解决方案:
1,是是重写 Context 的 getAsset 、 getResource 之类的方法,偷换概念,让插件读取插件里的资源,但缺点就是宿主和插件的资源 id 会冲突,需要重写 AAPT 。
2,另一种是重写 AMS中保存的插件列表,从而让宿主和插件分别去加载各自的资源而不会冲突。
3,就是打包后,执行一个脚本,修改生成包中资源id。
如何解决不同插件的开发人员的工作区问题
这时候就要用到 Gradle 脚本了,每个项目分别有各自的仓库,有各自不同的打包脚本,只需要把自己的插件跟宿主项目一起打包运行起来,而不用引入其他插件,还有更厉害的是,也可以把自己的插件当作一个 App 来打包并运行。
自定义gradle插件替换插件资源:
gradle自定义插件替换资源文件:
使用动态加载技术可以在 Android 应用运行时加载外部的 dex 文件,而通过网络下载新的 dex 文件并替换原有的 dex 文件就可以达到不安装新 APK 文件就升级应用(改变代码逻辑)的目的。
在 Android 中的 ClassLoader 机制主要用来加载 dex 文件,系统提供了两个 API 可供选择:
PathClassLoader:只能加载已经安装到 Android 系统中的 APK 文件。因此不符合插件化的需求,不作考虑。
DexClassLoader:支持加载外部的 APK、Jar 或者 dex 文件,正好符合文件化的需求,所有的插件化方案都是使用 DexClassloader 来加载插件 APK 中的 .class文件的。
在 Android 中实现插件化框架,需要解决的问题主要如下:
资源和代码的加载
Android 生命周期的管理和组件的注册
宿主 APK 和插件 APK 资源引用的冲突解决
亮点
支持 Android 四大组件,而且插件中的组件不需要在宿主 APK 中注册。
支持 Android 2.3 及以上系统,支持所有的系统 API。
插件与插件之间,插件与宿主之间的代码和资源完全隔阂。
实现了进程管理,插件的空进程会被及时回收,占用内存低。
面临的问题
机型适配(不是所有机器上都能行,因为大量用反射相关,如果rom厂商深度定制了framework层,反射的方法或者类不在,容易插件运用失败)
常用技术手段
Hook 机制:动态代理实现函数 hook ,Binder 代理绕过部分系统服务限制,IO 重定向(先获取原始 Object –> Read ,然后动态代理 Hook Object 后–> Write 回去,达到瞒天过海的目的)。
总结
正如开头所说,要实现插件化的框架,无非就是解决那典型的三个问题:插件代码如何加载、插件中的组件生命周期如何管理、插件资源和宿主资源冲突怎么办。每个框架针对这三个问题,都有不同的解决方案,同时呢,根据时间顺序,后出来的框架往往都会吸收已经出的框架精髓,进而修复那些比较有里程碑意义框架的不足。但这些框架的核心思想都是用到了代理模式,有的在表面层进行代理,有的则在系统应用层进行代理,通过代理达到替换和瞒天过海,最终让 Android 系统误以为调用插件功能和调用原生开发的功能是一样的,进而达到插件化和原生兼容编程的目的。
具体代码:
进阶资料:
1,Android插件化从入门到放弃-最强合集
https://www.jianshu.com/p/353514d315a7
2,包建强的无线技术空间,写给Android App 开发人员看的 Android 底层知识 置顶8篇
https://www.jianshu.com/u/52c01cf99e6a
3,Android插件化原理解析
https://blog.csdn.net/huangkun125
4,Android插件化:从入门到放弃
如果想使用插件化框架,一定要了解其中的实现原理,文档上描述的并不是所有的细节,很多一些属性什么的,以及由于其实现的方式造成一些特性的不支持。

ModularizationArchitecture 使用教程
国内适配性最好的动态权限申请框架,接口的封装和设计值得参考:
利用gradle命令查看module的依赖树
使用微信AndResGuard的混淆机制,
https://github.com/shwenzhang/AndResGuard
组件化中,如果每个module都使用自身混淆,则会出现重复混淆的现象,造成查询不到资源文件的问题,解决这个问题的关键是,需要保证在apk生成的时候有且只有一次混淆。
apk文件本质上是一个带签名信息的zip文件,符合zip文件的格式规范。
掌握多渠道打包的原理
混淆生成的文件路径:
build/outputs/mapping/project-name/release/...
dump.txt 包含目录类所有class文件的结构
mapping.txt 记录混淆后路径和原路径的映射关系
resource.txt 记录资源文件信息
seeds.txt 未混淆的类和成员
usage.txt 列出apk被删除的代码
什么是编译时注解?
java文件 -> .class文件,在java代码编写玩,就创建出对文件或类的索引关系,将索引关系编入到apk中,具体是提供了AP(注解处理器),在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成Java类来保存所有订阅者关于订阅的信息,这比使用反射速度快。