最近几年移动开发业界兴起了「 插件化技术 」的旋风,各个大厂都推出了本身的插件化框架,各类开源框架都评价自身功能优越性,使人应接不暇。随着公司业务快速发展,项目增多,开发资源却有限,如何能在有限资源内知足需求和项目的增加,同时又能快速响应问题和迭代新需求,这就是一个矛盾点。此时,插件化技术正好风生水起,去了解各个主流框架实现思路,看看能对目前工做是否有帮助,是颇有必要的。html
主要分为如下几个部分vue
百度百科里是这么定义插件的:「 是一种遵循必定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统平台下,而不能脱离指定的平台单独运行。」,也就是说,插件能够提供一种动态扩展能力,使得应用程序在运行时加载本来不属于该应用的功能,而且作到动态更新和替换。node
那么在 Android 中,何为「 插件化 」,顾名思义,就是把一些核心复杂依赖度高的业务模块封装成独立的插件,而后根据不一样业务需求进行不一样组合,动态进行替换,可对插件进行管理、更新,后期对插件也可进行版本管理等操做。在插件化中有两个概念须要讲解下:android
所谓宿主,就是须要能提供运行环境,给资源调用提供上下文环境,通常也就是咱们主 APK ,要运行的应用,它做为应用的主工程所在,实现了一套插件的加载和管理的框架,插件都是依托于宿主的APK而存在的。git
插件能够想象成每一个独立的功能模块封装为一个小的 APK ,能够经过在线配置和更新实现插件 APK 在宿主 APK 中的上线和下线,以及动态更新等功能。github
那么为什么要使用插件化技术,它有何优点,能给咱们带来什么样好处,这里简单列举了如下几点:面试
首先咱们要知道插件化技术是属于比较复杂一个领域,复杂点在于它涉及知识点普遍,不只仅是上层作应用架构能力,还要求咱们对 Android 系统底层知识须要有必定的认知,这里简单罗列了其中会涉及的知识点:mongodb
首先,要介绍的是 Binder ,咱们都知道 Android 多进程通讯核心就是 Binder ,若是没有它真的步履维艰。 Binder 涉及两层技术,你能够认为它是一个中介者模式,在客户端和服务器端之间, Binder 就起到中介的做用。若是要实现四大组件的插件化,就须要在 Binder 上作修改, Binder 服务端的内容没办法修改,只能改客户端的代码,并且四大组件的每一个组件的客户端都不同,这个就须要深刻研究了。学习Binder的最好方式是 AIDL ,这方面在网上有不少资料,最简单的方式就是本身写个 aidl 文件自动生成一个 Java 类,而后去查看这个Java类的每一个方法和变量,而后再去看四大组件,其实都是跟 AIDL 差很少的实现方式。编程
其次,是 App 打包的流程。代码写完了,执行一次打包操做,中途经历了资源打包、 Dex 生成、签名等过程。其中最重要的就是资源的打包,即 AAPT 这一步,若是宿主和插件的资源id冲突,一种解决办法就是在这里作修改。安全
第三, App 在手机上的安装流程也很重要。熟悉安装流程不只对插件化有帮助,在遇到安装 Bug 的时候也很是重要。手机安装 App 的时候,常常会有下载异常,提示资源包不能解析,这时须要知道安装 App 的这段代码在什么地方,这只是第一步。第二步须要知道, App 下载到本地后,具体要作哪些事情。手机有些目录不能访问, App 下载到本地以后,放到哪一个目录下,而后会生成哪些文件。插件化有个增量更新的概念,如何下载一个增量包,从本地具体哪一个位置取出一个包,这个包的具体命名规则是什么,等等。这些细节都必需要清楚明白。
第四,是 App 的启动流程。 Activity 启动有几种方式?一种是写一个 startActivity ,第二种是点击手机 App ,经过手机系统里的 Launcher 机制,启动 App 里默认的 Activity 。一般, App 开发人员喜闻乐见的方式是第二种。那么第一种方式的启动原理是什么呢?另外,启动的时候,Main 函数在哪里?这个 Main 函数的位置很重要,咱们能够对它所在的类作修改,从而实现插件化。
第五点更重要,作 Android 插件化须要控制两个地方。首先是插件 Dex 的加载,如何把插件 Dex 中的类加载到内存?另外是资源加载的问题。插件多是 Apk 也多是 so 格式,无论哪种,都不会生成 R.id ,从而没办法使用。这个问题有好几种解决方案。一种是是重写 Context 的 getAsset 、 getResource 之类的方法,偷换概念,让插件读取插件里的资源,但缺点就是宿主和插件的资源 id 会冲突,须要重写 AAPT 。另外一种是重写 AMS中保存的插件列表,从而让宿主和插件分别去加载各自的资源而不会冲突。第三种方法,就是打包后,执行一个脚本,修改生成包中资源id。
第六点,在实施插件化后,如何解决不一样插件的开发人员的工做区问题。好比,插件1和插件2,须要分别下载哪些代码,如何独立运行?就像机票和火车票,如何只运行本身的插件,而不运行别人的插件?这是协同工做的问题。火车票和机票,这两个 Android 团队的各自工做区是不同的,这时候就要用到 Gradle 脚本了,每一个项目分别有各自的仓库,有各自不一样的打包脚本,只须要把本身的插件跟宿主项目一块儿打包运行起来,而不用引入其余插件,还有更厉害的是,也能够把本身的插件看成一个 App 来打包并运行。
上面介绍了插件化的入门知识,一共六点,每一点都须要花大量时间去理解。不然,在面对插件化项目的时候,不少地方你会一头雾水。而只要理解了这六点核心,一切可迎刃而解。
在Android中应用插件化技术,其实也就是动态加载的过程,分为如下几步:
Android 项目中,动态加载技术按照加载的可执行文件的不一样大体能够分为两种:
第一点, Android 中 NDK 中其实就使用了动态加载,动态加载 .so 库并经过 JNI 调用其封装好的方法。后者通常是由 C/C++ 编译而成,运行在 Native 层,效率会比执行在虚拟机层的 Java 代码高不少,因此 Android 中常常经过动态加载 .so 库来完成一些对性能比较有需求的工做(好比 Bitmap 的解码、图片高斯模糊处理等)。此外,因为 .so 库是由 C/C++ 编译而来的,只能被反编译成汇编代码,相比中 dex 文件反编译获得的 Smali 代码更难被破解,所以 .so 库也能够被用于安全领域。
其二,“基于 ClassLoader 的动态加载 dex/jar/apk 文件”,就是咱们指在 Android 中 动态加载由 Java 代码编译而来的 dex 包并执行其中的代码逻辑,这是常规 Android 开发比较少用到的一种技术,目前说的动态加载指的就是这种。
Android 项目中,全部 Java 代码都会被编译成 dex 文件,Android 应用运行时,就是经过执行 dex 文件里的业务代码逻辑来工做的。使用动态加载技术能够在 Android 应用运行时加载外部的 dex 文件,而经过网络下载新的 dex 文件并替换原有的 dex 文件就能够达到不安装新 APK 文件就升级应用(改变代码逻辑)的目的。
因此说,在 Android 中的 ClassLoader 机制主要用来加载 dex 文件,系统提供了两个 API 可供选择:
在 Android 中实现插件化框架,须要解决的问题主要以下:
下面分析几个目前主流的开源框架,看看每一个框架具体实现思路和优缺点。
DL 动态加载框架 ( 2014 年末)
是基于代理的方式实现插件框架,对 App 的表层作了处理,经过在 Manifest 中注册代理组件,当启动插件组件时,首先启动一个代理组件,而后经过这个代理组件来构建,启动插件组件。 须要按照必定的规则来开发插件 APK,插件中的组件须要实现通过改造后的 Activity、FragmentActivity、Service 等的子类。
优势以下:
缺点以下:
DroidPlugin ( 2015 年 8 月)
DroidPlugin 是 360 手机助手实现的一种插件化框架,它能够直接运行第三方的独立 APK 文件,彻底不须要对 APK 进行修改或安装。一种新的插件机制,一种免安装的运行机制,是一个沙箱(可是不彻底的沙箱。就是对于使用者来讲,并不知道他会把 apk 怎么样), 是模块化的基础。
实现原理:
插件 Host 的程序架构:
优势以下:
缺点以下:
Small ( 2015 年末)
Small 是一种实现轻巧的跨平台插件化框架,基于“轻量、透明、极小化、跨平台”的理念,实现原理有如下三点。
架构图:
优势以下:
缺点以下:
与其余主流框架的区别:
DyLA : Dynamic-load-apk @singwhatiwanna DiLA : Direct-Load-apk @FinalLody APF : Android-Plugin-Framework @limpoxe ACDD : ACDD @bunnyblue DyAPK : DynamicAPK @TediWang DPG : DroidPlugin @cmzy, 360
DyLA | DiLA | ACDD | DyAPK | DPG | APF | Small | |
---|---|---|---|---|---|---|---|
加载非独立插件 | × | x | √ | √ | × | √ | √ |
加载.so后缀插件 | × | × | ! | × | × | × | √ |
Activity生命周期 | √ | √ | √ | √ | √ | √ | √ |
Service动态注册 | × | × | √ | × | √ | √ | x |
资源分包共享 | × | × | ! | ! | × | ! | √ |
公共插件打包共享 | × | × | × | × | × | × | √ |
支持AppCompat | × | × | × | × | × | × | √ |
支持本地网页组件 | × | × | × | × | × | × | √ |
支持联调插件 | × | x | × | × | × | × | √ |
ACDD | DyAPK | APF | Small | |
---|---|---|---|---|
插件Activity代码无需修改 | √ | √ | √ | √ |
插件引用外部资源无需修改name | × | × | × | √ |
插件模块无需修改build.gradle | × | x | × | √ |
VirtualAPK (2017年 6 月 )
VirtualAPK 是滴滴开源的一套插件化框架,支持几乎全部的 Android 特性,四大组件方面。
架构图:
实现思路:
VirtualAPK 对插件没有额外的约束,原生的 apk 便可做为插件。插件工程编译生成 apk后,便可经过宿主 App 加载,每一个插件 apk 被加载后,都会在宿主中建立一个单独的 LoadedPlugin 对象。以下图所示,经过这些 LoadedPlugin 对象,VirtualAPK 就能够管理插件并赋予插件新的意义,使其能够像手机中安装过的 App 同样运行。
特性以下:
四大组件均不须要在宿主manifest中预注册,每一个组件都有完整的生命周期。
theme
和LaunchMode
,支持透明主题;start
、stop
、bind
和unbind
,并支持跨进程bind插件中的Service;CRUD
和call
方法等,支持跨进程访问插件中的Provider。自定义 View
,支持自定义属性和style
,支持动画;PendingIntent
以及和其相关的Alarm
、Notification
和AppWidget
;Application
以及插件manifest中的meta-data
;so
。优秀的兼容性
AMS
和IContentProvider
,hook 过程作了充分的兼容性适配。入侵性极低
以下是 VirtualAPK 和主流的插件化框架之间的对比。
特性 | DynamicLoadApk | DynamicAPK | Small | DroidPlugin | VirtualAPK |
---|---|---|---|---|---|
支持四大组件 | 只支持Activity | 只支持Activity | 只支持Activity | 全支持 | 全支持 |
组件无需在宿主manifest中预注册 | √ | × | √ | √ | √ |
插件能够依赖宿主 | √ | √ | √ | × | √ |
支持 PendingIntent | × | × | × | √ | √ |
Android 特性支持 | 大部分 | 大部分 | 大部分 | 几乎所有 | 几乎所有 |
兼容性适配 | 通常 | 通常 | 中等 | 高 | 高 |
插件构建 | 无 | 部署aapt | Gradle插件 | 无 | Gradle插件 |
RePlugin (2017 年 7 月)
RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由360手机卫士的RePlugin Team研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。
框架图:
主要优点有:
极其灵活:主程序无需升级(无需在Manifest中预埋组件),便可支持新增的四大组件,甚至全新的插件
很是稳定:Hook 点仅有一处(ClassLoader),无任何 Binder Hook!如此可作到其崩溃率仅为“万分之一”,并完美兼容市面上近乎全部的 Android ROM。
特性丰富:支持近乎全部在“单品”开发时的特性。包括静态 Receiver、 Task-Affinity 坑位、自定义 Theme、进程坑位、AppCompat、DataBinding等。
易于集成:不管插件仍是主程序,只需“数行”就能完成接入。
管理成熟:拥有成熟稳定的“插件管理方案”,支持插件安装、升级、卸载、版本管理,甚至包括进程通信、协议版本、安全校验等。
数亿支撑:有 360 手机卫士庞大的数亿用户作支撑,三年多的残酷验证,确保App用到的方案是最稳定、最适合使用的。
主要是测试各个框架之间上手的容易度如何,并作不一样对比,这边写了两个 Demo 例子,一个是基于 Small 框架,一个基于 VirtualAPK 框架,从中能看出不一样。
Small 实践
要引用官方最新的版本,否则在宿主和插件合并build.gradle
的时候会出现一个 BUG,这是个坑位,注意行走。其次在模块命名上要遵循必定的规则,好比业务模块用 app. ,公共库模块用 lib. ,至关于包名 .app.,.lib. 。每次在插件中添加一个 activity 组件,都须要在宿主中配置路由,而后在从新编译插件一遍,否则直接运行的话,在宿主中是找到新添加的 activity 组件,会报该组件没在系统 manifest 中,因此每次新增或修改建议插件都从新编译一遍。官方里说了,对于 Service 支持不太友好,就没去实践了。
VirtualAPK 实践
有个坑须要注意的是构建环境,官方说明是要如下版本环境,Gradle 2.14.1 和 com.android.tools.build 2.1.3, 以前编译的是用最新的Gradle版本,致使一直有问题,至因而否有其余问题,能够看官方文档。
具体代码
Small Demo :
https://github.com/cr330326/MySmallVirtualAPK Demo :
https://github.com/cr330326/MyVirtualAPKDemo
正如开头所说,要实现插件化的框架,无非就是解决那典型的三个问题:插件代码如何加载、插件中的组件生命周期如何管理、插件资源和宿主资源冲突怎么办。每一个框架针对这三个问题,都有不一样的解决方案,同时呢,根据时间顺序,后出来的框架每每都会吸取已经出的框架精髓,进而修复那些比较有里程碑意义框架的不足。但这些框架的核心思想都是用到了代理模式,有的在表面层进行代理,有的则在系统应用层进行代理,经过代理达到替换和瞒天过海,最终让 Android 系统误觉得调用插件功能和调用原生开发的功能是同样的,进而达到插件化和原生兼容编程的目的
声明原做者:斜杠Allen
原文地址:http://www.apkbus.com/blog-94...
看完你就该会git了(手把手教你用vue+node+mongodb搭建一个小商城
欢迎关注个人微信公众号:终端研发部,有问题随时来撩我!