组件化
组件化和插件化最大区别
组件化在编译期可以动态添加和修改,运行期则不行。
插件化在编译期和运行期都可以动态添加和修改。
组件模块
组件Component是一种工具功能性质,而模块Module具有系统功能性质。例如jetpack中的构架组件。
参考链接
https://juejin.im/post/5e5f2efde51d4526d43f3b99?utm_source=gold_browser_extension
https://juejin.im/post/5e4d2256f265da575918d381#heading-5
安卓组件化之Application
原文链接
Application的定义
- 代表应用程序(即
Android App
)的类,也属于Android
中的一个系统组件 - 继承关系:继承自
ContextWarpper
类
1 | java.lang.Object |
Application的特点
Application是单例,一个App仅创建唯一一个Application。
其生命周期就是App的生命周期。
Application中常用的回调方法:
onCreate:创建应用程序时回调,回调时机早于任何Activity
作用:
初始化应用程序级别的资源。如全局对象、环境配置变量、图片资源初始化、推送服务的注册等。
数据共享、数据缓存
设置全局共享数据,如全局共享变量、方法等。(App杀死会被清空)
注意:不要执行耗时操作,否则会拖慢应用程序启动速度。
onTerminate:终止应用程序时调用,不能保证一定会被调用(在模拟器上会被调用,真机上不会被调用)
1 | // registerComponentCallbacks() & unregisterComponentCallbacks() |
onLowmemory:当后台应用程序终止,但前台应用程序内存不够,可在此方法内做些资源释放的操作。
onConfigurationChanged:配置发生时回调,如屏幕旋转灯。
onTrimMemory:通知应用的不同内存情况(以内存级别进行识别)
内存使用情况 级别 (级别越高越严重) |
含义 |
---|---|
TRIM_MEMORY_RUNNING_MODERATE | 内存不足级别:5 状态:应用程序处于前台运行 含义:应用程序正常运行,不会被杀掉。但当前内存有点低,系统开始杀死其他进行后台应用程序 |
TRIM_MEMORY_RUNNING_LOW | 内存不足级别:10 状态:应用程序处于前台运行 含义:应用程序正常运行,不会被杀掉。但当前内存非常低了,请必须释放你自身不必要的内存,否则会影响应用程序的性能你(如响应速度等等) |
TRIM_MEMORY_RUNNING_CRITICAL | 内存不足级别:15 状态:应用程序处于前台运行 含义:应用程序正常运行,但大部分其他后台程序已被杀死,请务必释放你自身不必要的内存,否则你也会被杀死。 |
TRIM_MEMORY_UI_HIDDEN | 内存不足级别:20 状态:应用程序处于前台运行 含义:系统内存已经非常低了,并将该应用程序从前台切换到后台,即收回UI资源 |
TRIM_MEMORY_BACKGROUND | 内存不足级别:40 状态:应用程序处于后台缓存 含义:系统内存已经较低了,该应用程序处于LRU缓存列表的最近位置,但不会被清理掉。此时应用释放掉一些较容易恢复的资源让手机的内存变得充足,从而让我们的程序更长时间地保留在缓存当中 |
TRIM_MEMORY_MODERATE | 内存不足级别:60 状态:应用程序处于后台缓存 含义:系统内存已经非常低了,该应用程序处于LRU缓存列表的中间位置,若手机内存再得不到释放,该应用程序有被系统杀死的风险 |
TRIM_MEMORY_COMPLETE | 内存不足级别:80 状态:应用程序处于后台缓存 含义:内存严重不足,该应用程序已经处于LRU缓存列表的最边缘位置,应用程序随时有被回收的风险,此时应该把一切可以释放的资源都释放从而避免被杀死 |
合并Application
子Module和主Module最终会合成一份AndroidManifest(app\build\intermediates\manifests\full\debug\AndroidManifest.xml
)
合并规则:
- 子Module有自定义Application,主Module没有。子Module的Application合并到最终的AndroidManifest文件中
- 子Module无自定义Application,主Module有。主Module合并到最终AndroidManifest中。
- 多个子Module有自定义Application,在解决冲突后会在最终合并的AndroidManifest中使用最后编译的Module中的Application
- 子Module有自定义Application,主Module也有。会提示要在主Module的AndroidManifest中加
tools:replace
。最终AndroidManifest使用的是主Module中自定义的Application。
动态配置Application
除了Application需要合并之外,在组件化过程中各个Module的初始化可以通过在主Module中反射完成各个Module的初始化。如:
1 | public class ModuleConfig{ |
以下代码放在可被所有子Module依赖的库中
1 | /** |
子Module继承BaseAppInit实现自己Module的初始化工作
1 | /** |
在主Module的自定义Application中通过反射创建各个子Module的初始化类对象,并调用其初始化方法
1 | /** |
另一种实现方式:创建基类BaseAppInit (给主Module和每个子Module的Application来继承);创建BaseApplication(给主Module继承)其中通过反射进行每个子Module的Application的初始化;最后在主Module中进行添加各个子Module
1 | /** |
1 | /** |
在主 Module 中实现自定义 Application 并注册每个子 Module 的初始化文件
1 | public class MApplication extends BbaseApplication{ |
在各个子 Module 中实现具体的初始化类
1 | public class ModuleTreeAppInit extends BaseAppInit{ |
注意点
宿主和组件有同名资源时
宿主取的是宿主的资源。但是跳转到组件时,组件的是同名资源,所以组件显示的还是宿主的资源内容,此时点击会报错。
需要把组件的资源名称改一下。最好是在组件的build.gradle
的android
中添加resourcePrefix "gank_" //给 Module 内的资源名增加前缀, 避免资源名冲突
1 | android { resourcePrefix "gank_" //给 Module 内的资源名增加前缀, 避免资源名冲突} |
加这个后,点开组件的资源文件的时候会报红色,但是不影响运行。但最好还是要改一下资源文件的名称(加前缀)
插件化
- 资源如何加载,资源冲突问题如何解决
- 代码如何加载访问
- 插件的管理后台包括的内容
- 插件的增量更新问题
- 加载插件中的so库
《Android插件化开发指南》
四大组件都需要插件化技术吗
游戏类App,有一套自己的在线更新流程,很多用的是Lua之类的脚本。
手机助手、手机卫士,这类App对Service、Receiver、ContentProvider的使用比较多。所以四大组件的插件化都必须实现。
音乐类、视频类、直播类App,除了使用比较多的Activity,对Service和Receiver的依赖很强。
电商类、社交类、新闻类、阅读类App,基本是Activity,其他三大组件使用不是很多,可以只考虑对Activity插件化的支持。
如果App中主要是Activity,那么选择静态代理that框架就够了
Android插件化技术的发展历史,基本上分为两大流派:静态代理和动态替换
(插件化不能上google play,要用RN替代)
双开和虚拟机
既然插件化会慢慢被RN所取代,那么插件化的未来是什么?答案是,虚拟机技术。
VirtualApp、DroidPlugin
Android底层知识
AMS、AIDL、Binder、PMS、双亲委托、ClassLoader等
底层知识分两种:
- 知道概念即可,比如Zygote,其实App开发人员是不需要了解Zygote的,知道有这么个东西是“孕育天地”的就够了,类似的还有SurfaceFlinger、WMS这些概念。
- 需要知道内部原理,比如Binder,关于Binder的介绍铺天盖地,但对于App开发者,需要了解的是它的架构模型,只要有Client、Server以及ServiceManager就足够了。
四大组件的底层通信机制都是基于Binder的,我们需要知道每个组件中,分别是哪些类扮演了Binder Client,哪些类扮演了Binder Server。知道这些概念有助于App开发人员进行插件化编程。
Binder原理
Binder分为Client和Server两个进程
Client和Server是相对的。谁发消息谁就是Client,接收消息的就是Server
Binder的组成
IPC:进程间通信;
ServiceManager:负责把每个Binder Server注册到一个容器中(ServiceManager比喻成电话局,记录着每个住宅的电话。张三(Binder Client)拨打给李四(Binder Server))
Binder的通信过程
图中的SM即为ServiceManager。我们看到,Client不可以直接调用Server的add方法,因为它们在不同的进程中,就需要Binder来帮忙了。
- 首先,Server在SM容器中注册
- 其次,Client若要调用Server的add方法,就需要先获取Server对象,但SM不会把真正的Server对象返回给Client,会把Server的代理对象Proxy返回给Client。
- 再次,Client调用Proxy的add方法,ServerManager会帮它去调用Server的add方法,并把结果返回给Client。
综上所述:
1)学习Binder是为了更好地理解AIDL,基于AIDL模型,进而了解四大组件的原理。
2)理解了Binder再看AMS和四大组件的关系,就像是Binder的两个进程Server和Client通信。
AIDL原理
AIDL是Binder的延伸。
Android的很多系统服务都是AIDL,比如剪贴板。
学习AIDL需要知道以下几个类:
- IBinder
- IInterface
- Binder
- Proxy
- Stub
当我们自定义一个aidl文件时(比如MyAidl.aidl,里面有一个sum方法), Android Studio会帮我们生成一个类文件MyAidl.java,如图2-3所示。
我们把MyAidl.java中的三个类拆开,就一目了然了,如下所示:
1 | public interface MyAidl extends android.os.IInterface{ |
生成的三个类都在一个文件里,避免当有多个AIDL类的时候,Stub和Proxy类重名的问题。
插件化
插件轮子对比
DLA | ACDD | DroidPlugin | APF | TPF | |
---|---|---|---|---|---|
加载非独立插件 | x | 支持 | x | 支持 | 支持 |
四大组件 | x | 支持 | x | 支持 | 支持 |
Application | x | x | x | 支持 | 支持 |
Notification | x | x | x | 支持 | 支持 |
组件完整生命周期 | x | 支持 | 支持 | 支持 | 支持 |
Service动态注册 | x | 支持 | 支持 | 支持 | 支持 |
插件可独立进程 | x | x | 支持 | 支持 | 支持 |
宿主能力与资源共享 | x | x | x | x | 支持 |
支持本地网页组件 | x | x | x | x | 支持 |
支持联调插件 | x | x | x | x | 支持 |
支持第三方启动插件Activity | x | x | x | x | 支持 |
支持第三方bind插件Service | x | x | x | x | 支持 |
显示协议框架 | x | x | x | x | 支持 |
随系统切换语言 | x | x | x | x | 支持 |
Tos控件库 | x | x | x | x | 支持 |
框架对比-small
DyLA | DiLA | ACDD | DyAPK | DPG | APF | Small | |
---|---|---|---|---|---|---|---|
加载非独立插件 | x | x | 支持 | 支持 | x | 支持 | 支持 |
加载.so插件 | x | x | ! | x | x | x | 支持 |
Activity生命周期 | 支持 | 支持 | 支持 | 支持 | x | 支持 | 支持 |
Service动态注册 | x | x | 支持 | x | x | 支持 | x |
资源分包共享 | x | x | ! | ! | x | ! | 支持 |
公共插件打包共享 | x | x | x | x | x | x | 支持 |
支持AppCompat | x | x | x | x | x | x | 支持 |
支持本地网页组件 | x | x | x | x | x | x | 支持 |
支持联调插件 | x | x | x | x | x | x | 支持 |