0%

第1章:组件化基础

你知道组件化吗

在项目开发中,一般将公用的代码提取出来用于制作基础库 Base Module ,将某些单独的功能封装到 Library module 中,根据业务来划分 module,组内每个人分别开发各自的模块,如下图所示:

项目原始架构

但是,随着时间推移,扩展了一些业务模块之后,模块间互相调用的情况就越来越多,结构可能就会很混乱了,并且,耦合可能会非常严重。这时候,新的规划规则出现了,这就是组件化、模块化 以及 插件化

  • 组件:指的是单一功能的组件,如视频组件(VideoSDK)、支付组件(PaySDK)等,每个组件都能单独抽出来制作成SDK
  • 模块:指的是独立的业务模块,如首页模块(HomeModule)、直播模块(LiveModule)等,模块相对组件来说粒度更大,它可能包含多种不同的组件

组件化和模块化都是为了代码重用和业务解耦,区别在于模块化是业务导向,组件化是功能导向。模块化和组件化的缺点在于:旧项目需要重新适配组件化。当项目的越来越大,方法数可能就会超过 65535,此时有两种选择: MultiDex 分包解决以及 插件化方法解决

基础组件化架构

我们用一个非常基础的组件化架构图来解释组件化基础:

基础的组件化架构

其中,基础层包含一些基础库和对基础库的封装(如图片加载、网络加载、数据存储等)、组件层包含一些简单的业务(如登录、图片浏览等)、应用层用于统筹全部组件,并输出生成App。虽然这个架构简陋,但是已经包含了组件化的内涵。

依赖

通过Android Studio ,我们有3中基本的依赖,如下代码:

1
2
3
4
5
dependencies {
compile fileTree(include: ['*.jar,*.aar'], dir: 'libs')
compile project(':base')
annotationProcessor 'com.alibba:arouter-compiler:1.1.1'
}

这 3 种依赖分别是(我自己给命名的) jar dependency、module依赖、仓库索引依赖。需要注意的是:

  • 读入自身目录使用的是 : fileTree 字段
  • 读入其他资源 module 使用的是: project 字段,而 “:base”中冒号表示文件目录与自己相同层级

聚合和解耦

最好能考虑插拔,不要移除某个业务项目多数模块就不能工作了。聚合和解耦是项目架构的基础

重新认识 AndroidManifest

manifest 字面意思就是货单、旅客名单,AndroidManifest 就是 Android 项目的声明配置文件。我们知道,这里面放的是 Android四大组件以及 自定义的 Application

如果项目中有多个 module,每个moudle都配有一份 manifest ,那么最终生成一个 App 时只会存在一个 AndroidManifest,因为这些 Androidmanifest 会合并,解决冲突后成一份。我们能在: app/build/intermediates/manifests/full/debug/Androidmanifest.xml 中找到这个最终合成的 AndroidManifest 文件。intermediates 目录是用来包含 App 生成过程中产生的“中间文件”

AndroidManifest属性汇总

当编译主 module 时,会将那些功能 module 重新编译,然后将成果(aar) 放到主 module 的 intermediates 目录中。

子module成果放入主module

可以看到,会在 app/build/intermediates/exploded-aar 中引用其最终生成的 aar 文件,exploded-aar 还包含了其他第三方仓库引用到的库

aar 文件解压能看到目录中一般包含 aidl、assets 等资源, classes.jar 是每个module 的真正代码,res包含功能 module 的资源,而每个 module 都有自己的 AndroidManifest ,即使 module 没有四大组件,在 AndroidManifest 中也依然带有 application 标识,甚至还会帮我们补全 use-sdk 信息。

AndroidManifest 属性变更

module 的 manifest 文件在合入的时候,最主要的差别在于,activity 声明的时候,name 属性不再是缩进,而是完整地址,当然了,剩下的其他四大组件也会一样,为什么会这样呢?因为 AndroidManifest 会引用多个 module 中的文件,需要知道具体路径,不然在编译器打包时招不到具体路径。如下所示:

1
2
3
4
5
<!--在module中写法-->
<activity android:name=".MainActivity"/>

<!--合并后-->
<activity android:name="com.example.MainActivity"/>

此外,还会补全一些属性,比如 icon 和 theme (如果没有在编写中指定)。权限最终也会被合并。当然也会有 Application 的合并(如果module中也注册了Application 的话)。

多个module中声明的相同权限,最终只会有一份,关于主题的声明,Activity 的主题都会引用自身module 声明的 主题,不声明当然就是使用默认主题了,主moudle 中声明 的 theme 将默认为整个 App 的 UI 主题风格。

注册Application

关于shareUid(题目自己加的)

通过声明 Shared User id,拥有同一个User id 的多个 App 可以配置成运行正在同一个进程中,所以默认可以互相访问任意数据。需要注意的是 如果只是在功能module 中声明 shareUid,那么最终并不会被何如最终的 AndroidManifest 中,只有主 module 的声明的 sharedUserId 才会最终打包到 AndroidManifest

你所不知道的 Application

看一下 Application 中比较重要的方法:

  • onTerminate:当终止应用程序时回调,但是不保证一定调用,比如,当程序被内核终止以便为其他应用释放空间时,就不会有提醒和这个回调
  • onLowMemory: 当后台程序已经终止且此时资源还是匮乏时执行该回调,一般应该在这里释放一些不必要的资源
  • onConfigurationChanged:配置改变时触发,例如手机屏幕旋转
  • registerActivityLifecycleCallbacks() :用于监听Activity 的生命周期,可以利用方法获取在栈顶的 Activity 对象,用于弹dialog 等

组件化 Application

这个小节把“注册Application”的内容一起合并进来了。注,这里说的 Application 是指 AndroidManifest 中的。

如果主工程创建一个 Application,Library 里面也声明了,那么在merge 的时候可能会出错,因为如果 Library 中定义了与 主项目 相同的属性(如android:icon 和 android:theme),此时就合并失败,并且提示可以用 tools:replace=”android:name” 来声明Application 是可被替换的。例如,我们在 application 标签下添加:

Tools:replace=”android:icon, android:theme” (多个属性的话,使用 “,” 分开)

App 最终只会允许声明一个 Application 到 AndroidManifest 中,如果存在多个 Application 的情况可以参考以下 Application 的替换规则如下:

  • 主module有,功能module 没有,那就用主的
  • 其中一个功能module有,主module没有,则用功能module的
  • 如果功能module中有多个Application,那么在解决冲突后,最终载入到后编译的module中
  • 若主module 有,功能module也有,解决冲突后,最后编译的主 module 的 Application 会在 AndroidManifest 里面。
谢谢你的鼓励