Android 架构 - 组件化

news/2024/11/14 13:10:40/

一、概念

组件化是基于可重用目的,对单个功能进行开发,提升复用性降低耦合度。多个功能组件起来就是一个业务组件,多个业务组件组合起来就是一个应用,因此去除了模块间的耦合,使得按业务划分的模块成了可单独运行的业务组件。(组件化只是一定程度上的独立,还是依附于整个项目中,想完全独立详见插件化)

  • 功能复用:每个组件都是一个单独的功能,可被不同的模块甚至不同的应用所使用。
  • 提高效率:每个组件都是一个可单独编译运行的APP,由于拆分使得代码量少,调试起来快。
  • 协同开发:组件之间相互独立,使得不同的开发人员能专注于各自的功能开发互不影响。

1.1 需要考虑的问题

组件分层、切换模式按需编译、组件通信(路由框架)、组件生命周期(组件在应用中存在的时间,组件是否可以按照需求动态使用,涉及到组件的加载卸载等管理问题。)

1.2 插件模式

在组件的构建脚本 build.gradle 中指定,指定为集成模式(Library)可以被其它组件调用,指定为组件模式(Application)可以独立运行调试。

com.android.application项目构建后输出 apk 包,在调试时是一个应用能单独编译运行。
com.android.library项目构建后输出 aar 包,在打包时是一个库文件集成到项目中。
com.android.test配置一个 Android Test 工程。
//写法一(和别的一起写)
plugins {id 'com.android.library'id 'org.jetbrains.kotlin.android'
}
//写法二(分开写)
apply plugin: 'com.android.library'

1.3 创建组件

File→New→New Module。业务组件需要调试选择【Phone & Tablet】,功能组件(最后还是通过业务组件来调试)基础组件只用来集成选【Android Library】。 

二、结构划分

依赖关系是上层依赖下层,修改频率是上层高于下层。对于业务组件由于存在页面跳转、方法调用、事件通信等问题需要使用路由通信,其它层组件不存在耦合问题封装成 Library 即可。

app壳主工程,应用的入口,将业务组件打包成一个APP(打包环境、签名、混淆、主题等配置工作)。
业务组件业务组件之间无直接关系,通过路由进行通信。既可以作为 Application 单独编译运行调试,又可作为 Library 集成到项目中。(主页、消息、商城)
功能组件功能组件是为了支撑业务组件的某些功能而独立划分出来的组件(对公用的功能进行封装),功能实质上跟项目中引入的第三方库是一样的。(业务组件可以直接依赖基础组件去实现功能)
基础组件实现路由、一般是第三方框架、第三方SDK,修改频率极低。(Retrofit、Glide)

三、统一配置

3.1 模式切换和版本管理 config.gradle

  1. 创建方式是对 Project 的 build.gradle 使用 Ctrl+C 和 Ctrl+V 文件命名为 “config.gradle”,清空里面的代码并同步一下,然后在 Project 的 build.gradle 中引用刚才创建的文件。
  2. 添加一个全局变量 “isDebug” 来控制组件模式的切换(true为组件模式会编译成Application,false为集成模式会编译成Library),同步一下后就可以在每个组件的 build.gradle 中读取并做相应的动态处理。(见下方5.5)
  3. 将 app壳 的版本移至 config.gradle 中并替换成引用。(见下方6.3)

3.2 依赖管理 Verison Catalogs

此前会将依赖项定义在上方的 config.gradle 中,不支持点击跳转和更新提示,且更新依赖会重构整个项目。

在项目上右键→New→File→命名为“libs.versions.toml”,在 settings.gradle 中的 <dependencyResolutionManagement> 标签下使用 <versionCatalogs> 引入所创建的文件。

versions声明依赖项的版本
libraries声明依赖的别名
bundles声明依赖的组名
plugins声明插件

四、基础组件

4.1 创建 lib_common

4.2 提供 BaseApplication

        在组件模式下,也会为了获取上下文或初始化资源而自定义 Application,但在集成模式下打包到一起时既没有作用还会冲突,因为应用最后只能有一个 Application。

        app壳工程和业务组件都依赖于 lib_common 功能组件,因此在其中提供一个全局的 BaseApplication 供其它组件继承。

4.3 统一权限声明 AndroidManifest

声明了项目中用到的所有权限 user-permission 和 uses-feature,这样其它组件就无需在自己的 AndroidManifest.xm 声明自己要用到的权限了。

 

4.4 统一第三方依赖 build.gradle

        统一所有组件中用到的第三方库和jar包,具体写在 config.gradle 中再引入。使用 imlemntation 添加的依赖只对当前组件有效,使用 api 添加的依赖对其上层组件同样有效,这样依赖于基础组件的组件就不用再添加相同的依赖了。

        项目中某个组件会被其它组件重复依赖,在集成构建应用时 Gradle 会剔除重复的 arr 包,这样就不会存在好几份重复的代码了。而第三方库和我们的项目可能都依赖了相同的库(例如 Glide 中也依赖了 OkHttp)就会导致重复加载,解决办法是找出相同的库根据组件名或包名排除。

  • test替换不了,引入的时候提示无法识别(2024-01-04) 

4.5 公共类

封装了项目中的 Base类、Utis工具类等,公用的 Widget控件、业务组件中都用到的数据也应放在这里(例如 SharedPreferences 和 DataBase 中的登录数据)。

4.6 资源文件

将公共的 drawable、shape、layout、strings、colors、dimens、styles、theme 等资源文件放在这里,提升复用性,保证主题统一、避免文件名冲突。必须放在组件中的话,一定要规范命名前缀。

五、核心组件(业务组件同理)

5.1 创建 module_core

5.2 创建 debug 包

在业务组件中创建 debug 包,用于存放只在组件模式下使用的文件(如程序入口 Launcher Activity、自定义Application),在集成模式下会被剔除不参与打包(见下方build.gradle中配置)。

在业务组件的 src/main/java 目录上右键→New→Package。(不推荐在组件的其它层级上创建目录存放,切换到Android视图不会显示)

5.3 自定义的 Application

继承自 lib_common 中提供的 BaseApplication,组件自定义的 Application 写在 debug 目录中,在集成模式下剔除。(别忘了在下方 debug 目录中的 AndroidManifest 中注册)

5.4 两份 AndroidManifest

        在组件模式下,也会因为<application>标签配置、自定义Application 、指定启动入口(Launcher Activity)而在 AndroidManifest 中进行注册,但在集成模式下打包到一起时存在重复和冲突(桌面上好几个启动图标、只能注册一个自定义的Application、同一个权限无须多次申请)。

        由于可以在 build.gradle 中动态指定组件的模式,因此可以分别为两种模式加载不同的 AndroidManifest。组件模式下的 AndroidManifest 写在 debug 目录中,在集成模式下剔除。

  1. 将 AndroidStudio 为组件自动创建的 AndroidManifest 复制一份到 debug 目录,对 <manifest> 标签添加 package 属性指定为业务组件的包名。 像单工程那样使用就行。
  2. 修改自动创建的 AndroidManifest,将 <application> 的属性(保留了theme是方便组件中用到该主题的Activity不用每个都要单独设置) 删除。module_core 不要删除程序入口(app壳搬运的启动入口是这里),其它业务组件删除程序入口只保留四大组件的注册,对 <manifest> 标签添加 package 属性指定为组件的包名。

5.5 修改 build.gradle

  1. 在组件模式下才是一个应用,因此需要动态配置组件模式和 applicationId。
  2. 在 <android> 标签下使用 <sourceSets> 动态指定不同模式下加载的 AndroidManifest 文件,并剔除 debug 包下的文件不参与集成模式下的打包。
  3. 将版本替换成 config.gradle 中定义的。
  4. 在 <dependencies> 中引入依赖。

5.6 程序入口 LauncherActivity

用于初始化数据后启动目标 Activity(不需要可以省略),因此写在 debug 目录下,不用 setContentView(),传入目标 Activity 需要的 Intent 参数即可。

5.7 获取当前组件模式

如果想在代码中通过获取当前组件的模式进行特定开发,可以在业务组件 build.gradle 中的 <defaultConfig> 标签下进行配置,会在 BuildConfig.java 类中生成对应的字段。

六、搬空app壳

组件化需要一个空壳,这个壳工程中不处理任何业务,也没有Activity,由它将各个业务模块组合起来构成一个完整的应用。

6.1 搬运程序入口 MainActivity

  1. 将 app 中的 MainActivity.class 和 activity_main.xml 剪切至 module_core 中。
  2. 将 AndroidManifest 中的 MainActivity 注册(包含程序入口)也剪切至 module_core 中只保留 <application> 标签。

6.2 自定义的 Application

必须继承自 lib_common 组件中的 BaseApplication(如果app壳工程中无需自定义的Application 可以直接在 AndroidManifest 中声明为 BaseApplication),因为只有这样在打包应用后才能让 BaseApplication 全局生效。

  • 可以在这里初始化项目中使用到的第三方SDK,还可以在这里解决 Android 引用方法数不能超过 65535 的限制,对崩溃事件的捕获和发送也可以在这里声明。 

6.2 修改 AndroidManifest

<application>标签的属性都是在app壳配置的,而其它组件也会有自己的清单,在集成模式下最终会打包合并成一个文件,需要解决属性重复(在上面5.4中用于集成模式的AndroidManifest的<application>标签并不会进行配置,但还是会配置theme),分别对 app 的 <manifest> 和 <application> 添加如下代码。

manifest 标签xmlns:tools="http://schemas.android.com/tools"
application 标签tools:replace="android:label,android:icon,android:theme,android:allowBackup"

6.3 修改 build.gradle

应用的打包签名、buildTypes、defaultConfig都需要在这里配置,而它的dependencies则需要根据 isDebug 的值分别依赖不同的组件,在组件模式下app壳只需要依赖 lib_common 组件,在集成模式下必须依赖所有声明的业务组件。

将 <defaultConfig> 和 <dependencies> 下的配置移动到 config.gradle 和 libs.versions.toml 中并引入,只在组件模式下引入业务组件。

七、对比

app壳LibraryApplication
build.gradle插件模式com.android.applicationcom.android.librarycom.android.application
applicationId 属性组件模式下有,集成模式下无。
sourceSets 块组件模式下调用 debug 包中的自定义 AndroidManifest,集成模式下调用 AndroidStudio 自动生成的并剔除 debug 包下的文件。
dependencies 块

组件模式下依赖所有业务组件,集成模式下只依赖基础组件。

基础组件添加所有组件用到的依赖,功能组件依赖基础组件。依赖基础组件,看需求依赖公共业务组件。
AndroidManifest<manifest> 标签添加 tools 属性避免合并冲突。设置 package 属性为自己包名。设置 package 属性为自己包名。
<application> 标签配置并添加 replace 属性避免合并冲突。可以只配置theme属性避免每个Activity都要设置主题。组件模式下配置,继承模式下可以只配置theme属性避免每个Activity都要设置主题。
自定义Application继承 BaseApplication提供 BaseApplication继承 BaseApplication
启动入口 LauncherActivity设为 MainActivity设为 debug 包中的 LauncherActivity,内容为空用来做初始化后跳转到 业务Activity。
权限
四大组件注册注册
资源文件、公共类基础组件存放都会用到的,功能组件存放自己专用的。存放自己专用的

http://www.ppmy.cn/news/1290639.html

相关文章

C语言——表达式的求值

表达式求值有以下几种决定因素。 一、操作符优先级和结合性 类别 操作符 结合性 后缀 () [] -> . - - 从左到右 一元 - ! ~ - - (type)* & sizeof 从右到左 乘除 * / % 从左到右 加减 - 从左到右 移位 << >> 从左到右 关系 < < > > 从左…

iOS 小组件开发

iOS14之后Apple引入了新的WidgetKit&#xff0c;舍弃了原有额TodayExtension。 开发准备&#xff1a; 新的WidgetExtension只能通过SwiftUI进行开发&#xff1b; Widget有三种尺寸&#xff1a;systemSmall、 systemMedium、systemLarge&#xff0c;三种尺寸对应固定的UI类型布…

微信小程序:图片处理

参考&#xff1a;image | 微信开放文档 1、针对image图片属性 <view class"page"><view class"page__hd"><text class"page__title">image</text><text class"page__desc">图片</text></vie…

38 调优kafka

操作系统调优 1.禁止atime更新&#xff0c;减少文件系统的写操作。 mount -o noatime 2.选择高性能的文件系统&#xff0c;如ext4或者XFS 3.swap空间设置&#xff0c;将swappniness设置成很小的一个值比如1&#xff5e;10&#xff0c;防止linux OOM Killer 开启随意杀掉进程。…

[Flutter]WindowsOS上运行遇到的问题总结

[Flutter]WindowsOS上运行遇到的问题总结 写在开头 Flutter项目已能在移动端完美使用后&#xff0c;想看看在桌面端等使用情况 基于Flutter3.0后已支持Windows/MacOS等桌面端&#xff0c;不过具体的系统&#xff0c;还需要看下官方文档解释。 这里抛出文档地址&#xff0c;可…

【已解决】You have an error in your SQL syntax

报错讯息 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘desc,target_url,sort,status,create_by,modify_by,created,last_update_time FROM…

RK3568平台 input输入子系统

一.input子系统简介 Input 子系统是管理输入的子系统&#xff0c; 和 pinctrl 和 gpio 子系统一样&#xff0c; 都是 Linux 内核针对某一类设备而创建的框架。 input 子系统处理输入事务&#xff0c; 任何输入设备的驱动程序都可以通过 input 输入子系统提供的接口注册到内核&…

学习Go语言Web框架Gee总结--前缀树路由Router(三)

学习Go语言Web框架Gee总结--前缀树路由Router router/gee/trie.gorouter/gee/router.gorouter/gee/context.gorouter/main.go 学习网站来源&#xff1a;Gee 项目目录结构&#xff1a; router/gee/trie.go 实现动态路由最常用的数据结构&#xff0c;被称为前缀树(Trie树) 关…