插件化的优势

本文参考资料:

  • 让用户不用重新安装 APK 就能升级应用功能,减少发版本频率,增加用户体验。

  • 提供一种快速修复线上 BUG 和更新的能力

  • 按需加载不同的模块,实现灵活的功能配置,减少服务器对旧版本接口兼容压力

  • 模块化、解耦合、并行开发、 65535 问题

需要掌握的知识

  • Binder

    要实现四大组件的插件化,就需要在Binder上做修改。Binder服务端的内容没办法修改,只能改客户端的代码。学习Binder的最好方式就是AIDL。你可以读到很多关于AIDL的资料,通过制订一个aidl文件自动生成一个Java类,研究一下这个Java类的每个方法和变量,然后再反过来看四大组件,其实都是跟AIDL差不多的方式。

    进程间通讯支持同步、异步、Binder、广播等。

    关于Binder系列的文章:

    1, 动脑学院安卓学院

    2,Binder之应用层精彩解析

    3,彻底掌握 Binder(已阅)

    4,Bind机制完全总结(已阅)

    5,带你起源Android Binder的设计 (已阅)

Binder的优势:

而Binder机制的UID/PID是由Binder机制本身在内核空间添加身份标识,安全性高;并且Binder可以建立私有通道,这是linux的通信机制所无法实现的(Linux访问的接入点是开放的)。

  • App 打包的流程

    执行一次打包操作,中途经历了资源打包、 Dex生成、签名等过程。其中最重要的就是资源的打包,即AAPT这一步,如果宿主和插件的资源id冲突,一种解决办法就是在这里做修改。

  • 熟悉App的安装流程

    手机安装 App 的时候,经常会有下载异常,提示资源包不能解析,这时需要知道安装 App 的这段代码在什么地方,这只是第一步。第二步需要知道, App 下载到本地后,具体要做哪些事情。手机有些目录不能访问, App 下载到本地之后,放到哪个目录下,然后会生成哪些文件。插件化有个增量更新的概念,如何下载一个增量包,从本地具体哪个位置取出一个包,这个包的具体命名规则是什么,等等。这些细节都必须要清楚明白。

1,APK安装过程源码解析

  • App 的启动流程

    Activity 启动有几种方式?一种是写一个 startActivity ,第二种是点击手机 App ,通过手机系统里的 Launcher 机制,启动 App 里默认的 Activity 。通常, App 开发人员喜闻乐见的方式是第二种。那么第一种方式的启动原理是什么呢?另外,启动的时候,Main 函数在哪里?这个 Main 函数的位置很重要,我们可以对它所在的类做修改,从而实现插件化。
    

    1, Launcher启动Activity的工作过程

    2,Android系统启动源码分析

  • 插件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 系统误以为调用插件功能和调用原生开发的功能是一样的,进而达到插件化和原生兼容编程的目的。

具体代码:

Small Demo:

VirtualAPK Demo:


进阶资料:

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生成的时候有且只有一次混淆。


批量打包

Android打包工具,100个渠道包只需要10秒钟

渠道包打包神器

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类来保存所有订阅者关于订阅的信息,这比使用反射速度快。

results matching ""

    No results matching ""