-
插件化的定义
插件化(Pluginization)是指以插件形式将某个功能集成到一个系统或应用程序的过程。通俗地说,就是把应用的某些功能从原应用中分离出来,作为独立的插件,进行封装和部署,使得用户可以在原应用中动态添加、更新或删除这些插件,而不必对原应用进行修改和重新编译。
-
插件化的优势
-
模块化开发和维护:通过将应用中的不同功能分解成独立的模块,可以使得开发者更加专注于每个模块的实现和维护,从而提高代码质量和可维护性。
-
动态更新和升级:对于应用程序中的某些功能模块,可以独立进行更新和升级,不会影响整个应用程序的发布和部署,大大提高了应用程序的可靠性和灵活性。
-
减少内存消耗和编译时间:由于插件化技术可以将应用程序的某些功能以插件的形式动态加载,因此在运行时只需要加载必要的插件,避免了不必要的内存消耗,同时也减少了应用程序编译的时间。
-
提升用户体验:通过插件化技术,可以将应用程序的某些功能模块以插件的形式单独打包、下载、安装和卸载,给用户带来了更好的使用体验,也增加了用户粘性。
-
节省开发成本:通过插件化技术,应用程序的不同功能模块可以由不同的开发者或团队进行并行开发,从而达到提高开发效率和节约开发成本的目的。
-
插件化框架对比
目前比较流行的 Android 插件化框架主要包括:VirtualApk、DroidPlugin、DynamicAPK、Ranger、Small、RePlugin 等。
下面是几个主要插件化框架的对比:
- VirtualApk
VirtualApk 是美团点评开源的一款插件化框架,其最大的优势在于兼容性和稳定性上表现出色,支持插件与宿主共用进程,能够避免进程通信所带来的性能问题。同时,VirtualApk 还提供了丰富的插件生命周期管理、资源隔离等功能,可实现精密的插件运行状态监测和异常响应机制。
- DroidPlugin
DroidPlugin 由360手机卫士开发并开源,是一款功能齐全的插件化框架,在集成、使用和文档方面都很完善。该框架能够灵活地控制每个插件的进程、权限、升级、优先级等,并通过 APK 加载方式使得插件启动更为简单快速。
- DynamicAPK
DynamicAPK 是一款社区贡献型的插件化框架,致力于提升 Android 应用的动态特性和组件化开发方式。该框架支持多种插件加载模式,支持 Activity、Service、Broadcast 等组件的扩展,并拥有相对友好的文档和示例代码。
- Ranger
Ranger 是一个国产插件化框架,由小米开发并推广。该框架精简、轻量且快速,控制面板比较简单易用,能够实现插件的异步加载和卸载等功能,同时还提供了插件异常信息报告、CRASH 上报等完整的调试机制。
- Small
Small 是一种基于微内核架构实现的插件化框架,由阿里巴巴出品并推广。该框架可以在应用程序中自由地添加和删除插件,更可将插件和宿主进行隔离,启动切换频繁,性能表现出色。
- RePlugin
RePlugin 是 Ctrip 出品的另一款优秀的插件化框架,与 VirtualApk 类似,其优势在于兼容性、稳定性及运行效率上的优异表现。该框架的 Model 插件和 View 插件分别使用不同的类加载器来实现隔离,支持框架与逻辑分离,在多线程及并发访问方面具备优秀的表现。
-
插件化流程
- 开发插件代码
首先需要开发出一个独立的插件,也就是一个 APK 包,拉通工程之后进行开发和测试。在开发过程中,必须考虑插件和宿主之间的交互方式,尽可能避免全局变量、单例、静态等影响插件与宿主的隔离。
- 宿主接入插件框架
通过引用插件框架库,在系统内部实现动态加载插件 APK 包。插件框架要求宿主程序自定义ClassLoader 和Resource来代替系统原有的ClassLoader和Resource。常见的插件框架有上文所述的 VirtualApk、DynamicAPK、RePlugin 等。
- 插件打包
由于插件和宿主属于两个不同的 APK 包,所以最终需要将插件打包成 apk 并进行签名。这里涉及到“v1”、“v2” 签名的问题,对应 Android Studio 中的两种打包方式 “debug”和“release”。
- 上传插件至服务器
可以将打包签名完成的插件直接上传至服务器,以供下载和安装,也可以打包成“patch”包形式,将更新信息存放在服务器,并根据用户需要实现实时下载和更新。
- 下载、加载插件
应用启动时,宿主会根据需要自行下载或是从服务器获取要加载的插件,将其解压并存放在设备某个位置中。同时可采取缓存策略,缓存已下载的插件,避免重复网络请求和流量消耗。
- 运行插件
插件经过加载之后,还需要进行一些必要的初始化操作,比如设置自定义类加载器、Context 上下文等,最终通过 Intent 启动 Activity 或 Service 等组件运行,并将返回结果传递回宿主。
-
插件化类加载原理
- 共享 ClassLoader 实现
这是一种最简单、最常见的插件化类加载方式,常用于 Dex 文件动态加载。该方法需要在宿主程序中创建一个共享的 ClassLoader,并且将其传递到宿主和插件中。当某个 Activity 或 Service 被启动时,都会优先从该 ClassLoader 中查找对应的类信息。
插件部分通过调用 Activity.getClassLoader() 取得宿主 ClassLoader(即共享ClassLoader) 的引用, 通过反射调用 DexPathList 中的 makeDexElements 方法取得当前 Dex 的所有 Class 对象, 最后又插入到“gDefault”
缺点:容易发生类命名冲突和资源耗尽导致崩溃等问题。
- 自定义 ClassLoader 实现
为了避免共享 ClassLoader 的相关问题,很多插件化框架选择使用自定义 ClassLoader 实现插件的加载,并将每个插件使用独立的 ClassLoader 加载,以保证插件与宿主之间的隔离性。
该方法相对于上一种方式更加灵活,能够更好地实现资源隔离、插件生命周期控制等功能。同时,这种方式还可以通过 Hook 系统 ClassLoader 的方式实现对系统类的修改与扩展。
缺点:需要处理一些细节问题,如插件无法共享宿主资源及系统资源、启动时间常常相对较长、适配插件中使用的第三方库等问题。