Android插件化方案实践

article/2025/8/5 6:37:35

一、插件化概述

 

1、插件化和组件化的区别

组件化是将一个app拆分为多个模块进行协作开发,每个模块都是一个单独的组件,这些组件可以相互依赖,也可以单独调试运行。但是最终发布的时候,这些组件会合并在一起,组成一个整体的apk,这就是组件化开发。

插件化开发和组件化是有所不同的,插件化开发就是将一个app拆分成多个模块,但是每一个模块都是一个apk,最终打包的时候将宿主apk和插件apk分开打包,独立分发。宿主apk发布到市场,插件apk通过动态下发到手机存储空间,然后进行插装操作,宿主apk就能够加载到这个插件包,完成整个业务流程链路的闭合。

 

2、插件化的优势

提高编译速度

开发过程中,每个插件都是独立编译运行的,会是当的提高我们的开发速度。

业务模块完全解耦

每个业务模块都是完全独立的,可以随意删除和增加某一部分的功能。

利于团队开发

每个团队负责自己的功能,减少沟通的成本。

动态更新,按需下载

不需要重新安装即可实现功能的升级,对于一些不常用的功能,可以让用户按需下载,缩减整个程序包的大小。

解决65535的限制

每个业务都是一个独立的apk,所以完全可以解决分包的问题。

 

3、主流的插件化方案

      插件化的实现思路有很多,市面上开源框架也比较多,其中以滴滴公司研发的一款插件化框架VirtualAPK做的相对较优秀,功能也比较强大。

特性

DynamicLoadApk

DynamicAPK

Small

DroidPlugin

VirtualAPK

支持四大组件

只支持Activity

只支持Activity

只支持Activity

全支持

全支持

组件无需在宿主manifest中预注册

×

插件可以依赖宿主

×

支持PendingIntent

×

×

×

Android特性支持

大部分

大部分

大部分

几乎全部

几乎全部

兼容性适配

一般

一般

中等

插件构建

部署aapt

Gradle插件

Gradle插件

最近腾讯推出了一个由Kotlin实现的插件化框架Shadow,这款框架号称是全动态、零反射、无Hack实现的新一代插件化技术,因为没有用到实战项目中,所以暂时还没有做深入研究。

 

二、插件化实现原理

通过上面的概述,可以知道插件化和组件化不同之处在于每一个插件都是一个可以独立安装运行的apk,在用户使用的过程中,只会安装宿主apk,其他的插件会通过网络等方式的下发到手机的内部存储中。所以想实现插件化,就必须将手机中的插件和宿主做关联,达到可以相互通信的效果,但是宿主模块和插件是不能直接进行相互通信的,下面我们会说明原因,所以我们需要一个中间件来作为媒介,这个中间件就是插件SDK。

所以在实现插件化的方案时,项目的结构搭建应该是分三部分:

宿主模块:app(主工程)

中间件:pluginSDK(插件化工具包)

插件模块:loginPlugin、registPlugin ……(拆分的业务插件)

因为宿主和插件模块只能做业务逻辑相关的功能,所以要想实现插件化,宿主必须通过pluginSDK来加载和调用插件,那么在pluginSDK中就必须解决以下几个问题:

  • 插件的类加载
  • 插件的生命周期
  • 插件的路由管理
  • 插件的资源管理
  • 插件的事件处理

 

1、插件的类加载

如果想在主模块中去使用用插件,就必须将插件加载到主模块中,这里就会用到类加载器。我们知道Android中的虚拟机是Dalvik,它不是标准的Java虚拟机,所以在类加载机制上,和Java中的类加载器是有一些区别的。

Java虚拟机运行的class字节码,Android虚拟机运行的是dex字节码。

   

Android中使用的类加载器是PathClassLoader和DexClassLoader,PathClassLoader只能加载已经安装到手机的dex,而DexClassLoader可以加载未安装的dex,所以我们可以通过DexClassLoader加载器来加载插件apk,在pluginSDK的PluginManager类中来实现插件加载初始化的方法

上图中的代码就是pluginSDK加载插件的核心实现,是对调用插件的初始化操作。这里主要是在宿主模块中调用这个初始化方法来加载插件,并获取插件中的dexClassLoader、packageInfo、resources三个对象,这三个对象我们会后续提供给代理类来使用

 

2、插件的生命周期

上面提到宿主模块和插件是不能直接进行相互通信的,这是因为插件中的Activity虽然继承了Activity,但是它只能看作是一个普通的类,并不是真正意义上的Activity,因为它不具备Activity最经典的两个特性:【生命周期】和【上下文】。这是为什么呢?因为我们都知道,插件是直接通过宿主app的动态加载来使用的,插件本身并没有经过Android系统安装的过程,那也就是说,插件本身没有经过AMS的处理,Context对象就是在AMS的main函数中返回的,并且四大组件也是由AMS来统一调度的,所以说没有经过AMS的处理的Activity是没有“灵魂”的,那怎么办呢?我们可以通过“代理伪装”的方式来强制赋予插件Activity生命周期的能力。

先在pluginSDK中创建一个PluginInterface接口,接口类中定义Activity具有的所有的生命周期函数和参数,用来模拟插件Activity的生命周期。

然后再在pluginSDK中创建一个BaseActivity来实现这个接口,并实现attach方法。attach方法中接收的是一个Activity实例,这样只要传入我们后续提到的ProxyActivity代理类实例,Context对象也就具备了。最后将插件Activity都继承这个BaseActivity即可。

 

3、应用的路由管理

插件加载完成,我们最常用的操作就是路由跳转,我们需要从宿主模块跳转到各个插件中去。如果用Intent的方式,那我们必须在宿主模块中的AndroidManifest中去注册插件中的Activity,这么做肯定是不合理的,如果引入第三方的路由框架,一个是违背了插件化框架设计的原则,另一个是会限制功能的开发,这显然也是不可取的。所以我们要在pluginSDK中创建一个ProxyActivity,让这个代理类来实现路由的跳转。

首先我们应该先实现从宿主模块到ProxyActivity的跳转逻辑

这里需要注意的是,我们获取跳转的Activity的时候是通过activities[0]来拿到的,这就要求我们在开发插件的时候必须将启动Activity放到该插件的AndroidManifest中的第一个节点,这算是一个隐性的规范。

然后再继续实现ProxyActivity类中的路由逻辑

根据插件Activity类的名称,可以去加载到该类的实例,因为BaseActivity已经实现了PluginInterface接口,也就意味这插件Activity属于PluginInterface的子类,所以可以通过结构引用的方式通过接口来调用插件Activity的生命周期函数,达到唤起插件Activity的作用。

 

4、插件的资源管理

解决了上面的几个,其实就可以从宿主模块中实现跳转到插件中了,但是插件中的Activity并不能正常的进行UI显示和事件的监听,是因为插件不具备上下文对象,也就无法使用任何与上下文相关的api,就拿setContentView(R.layout.activity_login)这个类来说,他的本质实现应该是this.setContentView(R.layout.activity_ login),所以他也是需要上下文对象的,前面为了解决这个问题,我们已经将代理类ProxyActivity的实例传递给BaseActivity,而插件Activity又继承自这个BaseActivity,所以也就意味着插件Activity中的上下文对象是ProxyActivity代理类的上下文,那这里肯定是有问题的,插件类用代理类的上下文去获取资源肯定是不正确的,我们必须要在代理类中拿到插件类的资源并给到插件类才行。

上面我们已经在PluginManager中初始化并获取到了插件类的三个对象,分别是:dexClassLoader、packageInfo、resources,那我们可以在代理类中去重写getResources()方法并返回PluginManager.getInstance().getResources(),并且我们还需要在BaseActivity中重写setContentView()方法,插件中才能被调用执行,UI资源才能被加载成功。

 

5、插件的事件处理

通过上面的操作,我们已经可以从宿主模块跳转到对应的插件中了,但是如果在插件中的做一些与上下文相关的操作(如:findViewById),还是要调用BaseActivity中对应的方法,所以需要重写大量的Activity的方法来给子类做支持,如下图,that就是代理类传递给BaseActivity的上下文对象。这一系类的操作本质就是插件不具备上下文对象,我们只能强制给插件赋予这个属性。

 

三、探究最后

关于android的插件化原理,本次的研究大概就是以上几点,本文是从apk动态加载的层面对插件化技术的一个研究,通过代理和反射获取到插件的实例,距离商用的技术方案,还需要大量的细节优化和测试。对于市面上的插件化技术方案,各个大厂也是研发了各自优秀的技术方案,比如滴滴公司的VirtualAPK插件化方案尤为优秀。

动态加载技术会围绕着插件Activity不具备生命周期这个特征做大量的手动映射和管理,开发侵入性比较强,而VirtualAPK通过替换了系统的Instrumentation,hook了Activity的启动和创建,省去了手动管理插件Activity生命周期的繁琐,让插件Activity像正常的Activity一样被系统管理,并且插件Activity在开发时和常规一样,即能独立运行又能作为插件被主工程调用,这种方案的实现难度无疑会更大。未来更多的插件化技术会被研究和开发出来,如果插件化技术稳定发展到了一定的程度,很可能会影响App以后的开发方式。

 

参考:

《Activity启动流程》
《Android apk动态加载机制的研究》
《Hook机制-AMS&PMS》

 


http://chatgpt.dhexx.cn/article/d74Tny2F.shtml

相关文章

Android手机开发课程设计之记事本

一、需求分析 1.1业务需求分析 近年来,随着生活节奏的加快,工作和生活的双重压力全面侵袭着人们,如何避免忘记工作和生活中的诸多事情而造成不良的后果就显得非常重要。为此我们开发一款基于Android系统的简单记事本,其能够便携…

MATLAB图形绘制--添加图例

添加图例 专业的图像总是附有图例来告诉读者曲线表示什么,下面我们将绘制两个表示势能的函数,他们由双曲三角函数sinh(x)和cosh(x)来定义,X的定义域为0~2,首先我们定义X: >> x [0:0.01:2];然后表示…

matlab 图例自定义,matlab中如何自定义图例_常见问题解析

pd接口是什么口_常见问题解析 pd接口也是“Type-C”的接口,支持扩充协议,可以给手机笔记本充电,也可以传输数据;而“Type-C”中的PD的意思指的是“USB Power Delivery”功率传输协议。 matlab中如何自定义图例?Matlab如…

Matlab 画多个图例( Plot multiple legends )

用matlab 画图时,发现线太多,生成的图例,遮盖了曲线。于是想画成多个图例,然后可以自由拖动。 废话不多说,2)代码(设置多个图例的部分在最后20行); 1)效果。 …

matlab之图例legend的数字变量显示

legend是matlab的图例使用函数,其主要的只用方式为legend(内容1,内容2) 本文主要介绍一下legend如何引用数组的数字表现形式。 例如,若想要画一个滤波器的不同反馈系数的频率响应曲线,在设置条例内容时,需要手动输入反馈系数K的数…

MATLAB图例变成一列变多列的方法

小编在绘制GPS数据图像时,发现图例中元素个数有31个,当采用默认生成图例时,只有一列图例,显示结果如下 图例非常难看,而且占位子,如何让图例从一列变成多列呢? 这里小编给大家提供一个思路&…

matlab把图例放在左边,如何将图例放在p之外

如何将图例放在p之外 我有一系列20个图(不是子图)可以在一个图中制作。 我希望传说能够超越盒子。 与此同时,我不想改变轴,因为图形的大小减少了。 请帮助我以下查询: 我想将情节框保留在情节区域之外。 (我希望传说位于情节区域的右侧)。 无论如何,我减少了图例框内文本的…

matlab之在坐标区上添加图例函数legend

目录 一、功能 二、语法 1.legend(label1,...,labelN) 2.legend(labels) 3.legend(subset,___) 4.legend(target,___) 5.legend(___,Location,lcn) 6.legend(___,Orientation,ornt) 7.legend(___,Name,Value) 8.legend(bkgd) 9.lgd legend(___) 10.legend(v…

Matlab作图后的各种调整方法——线条、坐标、标题、图例

Matlab作图后的各种调整方法——线条、坐标、标题、图例 文章目录 Matlab作图后的各种调整方法——线条、坐标、标题、图例一 , 写在前面1.整个图窗 Figure(gcf)2.我们使用命令做出的线条 Line,例如plot命令3.坐标轴 Axes (gca) 二…

Matlab图例Legend多行排布、字体格式

适用于图例文字多,简单排布效果差的情形 1. 绘制图形,添加图例 %数据 x[1 2 3 4]; y1[16 2 3 13]; y2[5 11 10 8]; y3[9 7 6 12]; y4[4 14 15 1]; %画图plot(x,y1,r-o); %红色,实线,圆圈 hold on; plot(x,y2,k-d…

在MATLAB中的图例标注及实例说明

1.基本绘图函数 plot(Y):其中输入参数Y就是Y轴的数据,一般习惯性输入向量 plot(X1,Y1,LineSpec,...,Xn,Yn,LineSpec):LineSpec为选项(开关量)字符串,用于设置曲线颜色、线型、数据点等;LineSpec的标准设定…

【Matlab】画图时去掉某些图例

1. 什么是legend函数? 在Matlab中,legend函数用于在图形中添加图例,以便更好地理解和解释数据。图例提供了与图形相关的标识,使观察者能够了解图形中不同元素的含义。 legend 函数的语法如下: legend(标签1, 标签2, …

【Matlab】论文各种图例配色Matlab绘制

1. Matlab 绘图 1.1. Plot 函数 x-pi:pi/10:pi; %以pi/10为步长 ytan(sin(x))-sin(tan(x)); %求出各点上的函数值 plot(x,y,--rs,... %绘制红色的虚线,且每个转折点上用正方形表示。LineWidth,2,... % 设置线宽为2Marke…

matlab图例使用技巧

matlab图例使用技巧 1 图形曲线1.1 曲线形状1.2 曲线颜色1.3 曲线粗细1.4 同时改变曲线的形状,粗细,颜色 2 图形字体大小 1 图形曲线 1.1 曲线形状 1.2 曲线颜色 颜色的改变 可以 通过改变R-G-B 的值改变线条的颜色: matlab命令 &#xff1…

如何使用Matlab绘制图形并标记图例

当我们在写cscd、EI或者SCI论文时,常常需要放置一些图片,通常情况下需要对图片进行一些处理,对图像横纵坐标、曲线进行标记。本文阐述如何使用Matlab来实现这些功能。 1、如何使用Matlab绘制相关曲线图 在Matlab中,常采用plot函数…

MATLAB中标注图例

当在一幅图中出现多种图形中,用户可以根据自己的需要,利用legeng命令对不同的图例进行说明。它的使用格式如下: 调用格式 说明 legend(string1,stri…

MATLAB的二维线图绘图函数plot()实例积累

目录 1 plot(Y)2 plot(X,Y)3 plot(___,Name,Value)4 坐标轴标注和图形标题5图例标注6 目前MATLAB的文本已支持Latex公式输入7 控制坐标轴范围 1 plot(Y) plot(Y) 创建 Y 中数据对每个值索引的二维线图。 如果 Y 是向量,x 轴的刻度范围是从 1 至 length(Y)&#xff…

Matlab绘图设置任意位置的图例,多个图例设置

Matlab绘图设置任意位置的图例,多个图例设置 Matlab绘图的图例,可能最普遍的是这种,只有一个图例,竖直方向排列,或者水平方向排列: clc; clear all; close all; t 0:0.001:6; A sin(t); B cos(t); fi…

Matlab 图例 位置的不同命令

Matlab 图例 位置的不同命令 本文是转载的,我怕忘了,所以转载到我的页面来 原文:http://blog.sina.com.cn/s/blog_7db803c10102weyk.html 转载1: https://www.cnblogs.com/xym4869/p/12243271.html Matlab中legend默认的位置在…

用数组的reduce方法实现map方法

what 首先我们需要理解reduce这个方法的语法: arr.reduce(callback,init) //详细写法 arr.reduce((prev,cur,index,arr){... },[])* callback (执行数组中每个值的函数,包含四个参数)1、previousValue (上一次调用回…