Android组件化框架设计与实践

同样这样的问题在我们工作具体项目中处处碰到,就拿我们组内负责的某个移动端 APP 来说,就碰到如下几个问题:

  1. 代码量膨胀,不利于维护,不利于新功能的开发。

  2. 项目工程构建速度慢,在一些电脑上写两句代码,重新编译测试的话编译速度起码 10-20 分钟,有的甚至更长。

  3. 不同模块之间代码耦合严重,比如消息模块严重耦合视频模块,如果修改视频模块,相应的消息模块也需要修改,不然会产生一连串问题。

  4. 每个模块之间都有引用第三方库,但有些第三方库版本不一致,导致打包 APP 时候代码冗余,容易引起版本冲突。

  5. 有些定制项目如果只需要消息模块,其他模块不需要的话,做不到按需加载打包,因为模块之间有互联依赖。

  6. 现有项目基于以前其他人项目基础上开发,经手的人次过多,存在着不同的代码风格,项目中代码规范乱,类似的功能写法却不一样,导致不统一。

项目工程架构模式改变是大势所趋,那又该如何做呢?那就是:打造组件化开发框架,用以解决目前所面临问题,在讲解如何打造之前,需要谈谈组件化概念,组件化框架是什么。

概念,组件化是什么

问:什么是组件,什么是组件化?

答:在软件开发领域,**组件(Component)**是对数据和方法的简单封装,功能单一,高内聚,并且是业务能划分的最小粒度。举个我们生活中常见的例子就是电脑主板上每个元件电容器件,每个元件负责的功能单一、容易组装、即插即拔,但作用有限,需要一定的依赖条件才可使用。如下图:

电容元件

那么同样,组件化 就是基于组件可重用的目的上,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,使得整个软件系统也做到电路板一样,是单个或多个组件元件组装起来,哪个组件坏了,整个系统可继续运行,而不出现崩溃或不正常现象,做到更少的耦合和更高的内聚。

问:组件化、模块化容易混淆,两者区别又是什么?

答:模块化就是将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块我们相对熟悉,比如登录功能可以是一个模块,搜索功能可以是一个模块等等。而组件化就是更关注可复用性,更注重关注点分离,如果从集合角度来看的话,可以说往往一个模块包含了一个或多个组件,或者说模块是一个容器,由组件组装而成。简单来说,组件化相比模块化粒度更小,两者的本质思想都是一致的,都是把大往小的方向拆分,都是为了复用和解耦,只不过模块化更加侧重于业务功能的划分,偏向于复用,组件化更加侧重于单一功能的内聚,偏向于解耦。

问:组件化能带来什么好处?

答:简单来说就是提高工作效率,解放生产力,好处如下:

  • 代码简洁,冗余量少,维护方便,易扩展新功能。

  • 提高编译速度,从而提高并行开发效率。

  • 避免模块之间的交叉依赖,做到低耦合、高内聚。

  • 引用的第三方库代码统一管理,避免版本统一,减少引入冗余库。

  • 定制项目可按需加载,组件之间可以灵活组建,快速生成不同类型的定制产品。

  • 制定相应的组件开发规范,可促成代码风格规范,写法统一。

  • 系统级的控制力度细化到组件级的控制力度,复杂系统构建变成组件构建。

  • 每个组件有自己独立的版本,可以独立编译、测试、打包和部署。

设计,构建组件化框架

回到刚开始讲的 APP 单一工程模式,看张常见 APP 单一工程模式架构图:

APP单一工程模式架构

上图是目前比较普遍使用的 Android APP 技术架构,往往是在一个界面中存在大量的业务逻辑,而业务逻辑中充斥着各种网络请求、数据操作等行为,整个项目中也没有模块的概念,只有简单的以业务逻辑划分的文件夹,并且业务之间也是直接相互调用、高度耦合在一起的。单一工程模型下的业务关系,总的来说就是:你中有我,我中有你,相互依赖,无法分离。如下图:

业务模块互相耦合

组件化的指导思想是:分而治之,并行开发,一切皆组件。要实现组件化,无论采用什么样的技术方式,需要考虑以下七个方面问题:

  1. 代码解耦。如何将一个庞大的工程分成有机的整体?

  2. 组件单独运行。因为每个组件都是高度内聚的,是一个完整的整体,如何让其单独运行和调试?

  3. 组件间通信。由于每个组件具体实现细节都互相不了解,但每个组件都需要给其他调用方提供服务,那么主项目与组件、组件与组件之间如何通信就变成关键?

  4. UI 跳转。UI 跳转指的是特殊的数据传递,跟组件间通信区别有什么不同?

  5. 组件生命周期。这里的生命周期指的是组件在应用中存在的时间,组件是否可以做到按需、动态使用、因此就会涉及到组件加载、卸载等管理问题。

  6. 集成调试。在开发阶段如何做到按需编译组件?一次调试中可能有一两个组件参与集成,这样编译时间就会大大降低,提高开发效率。

  7. 代码隔离。组件之间的交互如果还是直接引用的话,那么组件之间根本没有做到解耦,如何从根本上避免组件之间的直接引用,也就是如何从根本上杜绝耦合的产生?

组件化架构目标:告别结构臃肿,让各个业务变得相对独立,业务组件在组件模式下可以独立开发,而在集成模式下又可以变为 AAR 包集成到“ APP 壳工程”中,组成一个完整功能的 APP。

先给出框架设计图,然后再对这七个问题进行一一解答。

A组件化框架架构

从图中可以看到,业务组件之间是独立的,互相没有关联,这些业务组件在集成模式下是一个个 Library,被 APP 壳工程所依赖,组成一个具有完整业务功能的 APP 应用,但是在组件开发模式下,业务组件又变成了一个个 Application,它们可以独立开发和调试,由于在组件开发模式下,业务组件们的代码量相比于完整的项目差了很远,因此在运行时可以显著减少编译时间。

各个业务组件通信是通过路由转发,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NdemG3Lx-1615520318619)(https://upload-images.jianshu.io/upload_images/788498-2c26405226fd9ed8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

这是组件化工程模型下的业务关系,业务之间将不再直接引用和依赖,而是通过“路由”这样一个中转站间接产生联系。

那么针对以上提出的七个问题,具体解决如下:

1,代码解耦问题

对已存在的项目进行模块拆分,模块分为两种类型,一种是功能组件模块,封装一些公共的方法服务等,作为依赖库对外提供,一种是业务组件模块,专门处理业务逻辑等功能,这些业务组件模块最终负责组装APP。

2,组件单独运行问题

通过 Gradle 脚本配置方式,进行不同环境切换。比如只需要把 Apply plugin: ‘com.android.library’ 切换成Apply plugin: ‘com.android.application’ 就可以,同时还需要在 AndroidManifest 清单文件上进行设置,因为一个单独调试需要有一个入口的 Activity。比如设置一个变量 isModule,标记当前是否需要单独调试,根据isModule 的取值,使用不同的 gradle 插件和 AndroidManifest 清单文件,甚至可以添加 Application 等 Java 文件,以便可以做一下初始化的操作。

3,组件间通信问题

通过接口+实现的结构进行组件间的通信。每个组件声明自己提供的服务 Service API,这些 Service 都是一些接口,组件负责将这些 Service 实现并注册到一个统一的路由 Router 中去,如果要使用某个组件的功能,只需要向Router 请求这个 Service 的实现,具体的实现细节我们全然不关心,只要能返回我们需要的结果就可以了。在组件化架构设计图中 Common 组件就包含了路由服务组件,里面包括了每个组件的路由入口和跳转。

4,UI 跳转问题

可以说 UI 跳转也是组件间通信的一种,但是属于比较特殊的数据传递。不过一般 UI 跳转基本都会单独处理,一般通过短链的方式来跳转到具体的 Activity。每个组件可以注册自己所能处理的短链的 Scheme 和 Host,并定义传输数据的格式,然后注册到统一的 UIRouter 中,UIRouter 通过 Scheme 和 Host 的匹配关系负责分发路由。但目前比较主流的做法是通过在每个 Activity 上添加注解,然后通过 APT 形成具体的逻辑代码。目前方式是引用阿里的 ARouter 框架,通过注解方式进行页面跳转。

5,组件生命周期问题

在架构图中的核心管理组件会定义一个组件生命周期接口,通过在每个组件设置一个配置文件,这个配置文件是通过使用注解方式在编译时自动生成,配置文件中指明具体实现组件生命周期接口的实现类,来完成组件一些需要初始化操作并且做到自动注册,暂时没有提供手动注册的方式。

6,集成调试问题

每个组件单独调试通过并不意味着集成在一起没有问题,因此在开发后期我们需要把几个组件机集成到一个 APP 里面去验证。由于经过前面几个步骤保证了组件之间的隔离,所以可以任意选择几个组件参与集成,这种按需索取的加载机制可以保证在集成调试中有很大的灵活性,并且可以加大的加快编译速度。需要注意的一点是,每个组件开发完成之后,需要把 isModule 设置为 true并同步,这样主项目就可以通过参数配置统一进行编译。

7,代码隔离问题

如果还是 compile project(xxx:xxx.aar) 来引入组件,我们就完全可以直接使用到其中的实现类,那么主项目和组件之间的耦合就没有消除,那之前针对接口编程就变得毫无意义。我们希望只在 assembleDebug 或者 assembleRelease 的时候把 AAR 引入进来,而在开发阶段,所有组件都是看不到的,这样就从根本上杜绝了引用实现类的问题。

目前做法是主项目只依赖 Common 的依赖库,业务组件通过路由服务依赖库按需进行查找,用反射方式进行组件加载,然后在主工程中调用组件服务,组件与组件之间调用则是通过接口+实现进行通信,后续规划通过自定义Gradle 插件,通过字节码自动插入组件的依赖进行编译打包,实现自动筛选 assembleDebug 或 assembleRelease 这两个编译命任务,只有属于包含这两个任务的命令才引入具体实现类,其他的则不引入。

代码,具体项目实践

一,创建工程

1,APP空壳工程

通过AndroidStudio创建一个APP空壳工程,如图:

APP空壳工程

然后在 APP 工程添加依赖具体业务组件 Module。比如:

依赖关系

2,具体业务组件Module

需要遵循一定组件命名规范,为何需要规范呢,因为需要通过组件命名规范来约束和保证组件的统一性和一致性,避免出现冲突。比如登陆组件,那么名称:b(类型)-ga(部门缩写)-login(组件名称),这就是我们基于共同的约定进行命名的,为后期维护和扩展都带来辨识度。

Login业务组件

二,业务组件配置文件

1,build.gradle配置文修改。如下:

if (isModule.toBoolean()) {

apply plugin: ‘com.android.application’

} else {

apply plugin: ‘com.android.library’

}

android {

compileSdkVersion rootProject.ext.compileSdkVersion

buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {

minSdkVersion rootProject.ext.minSdkVersion

targetSdkVersion rootProject.ext.targetSdkVersion

if (isModule.toBoolean()) {

applicationId “com.hik.ga.business.login”

versionCode 1

versionName “1.0”

} else {

//ARouter

javaCompileOptions {

annotationProcessorOptions {

arguments = [ moduleName : project.getName() ]

}

}

}

}

sourceSets {

main {

if (isModule.toBoolean()) {

manifest.srcFile ‘src/main/module/AndroidManifest.xml’

} else {

manifest.srcFile ‘src/main/AndroidManifest.xml’

//集成开发模式下排除debug文件夹中的所有Java文件

java {

exclude ‘debug/**’

}

}

}

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’

}

}

}

dependencies {

implementation project(‘:b-ga-common-function-comlib’)

if (!isModule.toBoolean()) {

annotationProcessor “com.alibaba:arouter-compiler:${rootProject.annotationProcessor}”

}

}

这里需要有几点说明一下:

1,通过 isModule.toBoolean() 方法来进行组件间集成模式和组件模式的切换,包括模块是属于Application 还是 Library,由于集成了 ARouter,所以需要对 ARouter 配置文件进行处理。

2,如果组件模式下, 则需要重新设置 AndroidManifest.xml 文件,里面配置新的Application路径。比如Login组件单独运行 AndroidManifest 清单文件

<manifest xmlns:android=“http://schemas.android.com/apk/res/android”

package=“hik.ga.business.applogin” >

<application

android:name=“debug.LoginApplication”

android:allowBackup=“false”

android:icon=“@mipmap/ic_launcher”

android:label=“@string/login_btn_str”

android:supportsRtl=“true”

android:theme=“@style/Theme.AppCompat.Light.NoActionBar”>

<activity

android:name=“hik.ga.business.applogin.login.views.LoginActivity”

android:label=“@string/login_btn_str”

android:launchMode=“singleTop”

android:screenOrientation=“portrait”

android:theme=“@style/AppTheme.NoActionBarFullScreen”>

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
轻大家的负担。**

[外链图片转存中…(img-dtGe5IU7-1715434696213)]

[外链图片转存中…(img-ivHYTOvU-1715434696215)]

[外链图片转存中…(img-iEb3o7pR-1715434696216)]

[外链图片转存中…(img-gKTRbjg5-1715434696217)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 30
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值