Android 架构系列:
Android 架构一:Android 架构浅析
Android 架构二:纵向横向结合构建项目
Android 架构三:组件化思想
Android 架构四:组件化架构实战
Android 架构五:MVVM+Data Binding构建项目
……
一、前言,目前项目架构的困境
承接上一文,我们开始组件化的实战。
由于以前的员工对项目的整体规划不足,采取了单工程的方式搭建架构,随着时间的发展,项目冗余功能和代码增多,让app的运行bug频出以及维护成本和维护难度的增加。
记得在14年的时候做过一次基于Eclipse的项目的结构优化,主要是分为是三个工程:
Library: 基本库工程
后台sdk:服务端SDK工程
主工程: 客户端展示工程
服务端SDK与客户端分开进程,后台sdk依赖Library,主工程依赖后台SDK,这样一个粗略的组件化调整,使得项目维护与管理成本减低了。
两三年过去了,现在的项目还在用着那套框架,随着APP越来越庞大、开发成员越来越多,并且存在多个业务并发进行的情况,这个结构也越来越不适应了,越来越难维护了,于是下定决心再一次重构,搭建组件化项目框架。
二、解决办法,组件化架构
什么是组件化
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,它是一种高效的处理复杂应用系统,更好的明确功能模块作用的方式,分离组件边界和责任,便于独立升级和维护。
大家都知道现在组件化很火,几乎每家公司都在的使用。为什么他们都要抢着去搭建组件化架构,个人感觉主要是因为它有以下优点:
- 1、提高了代码的复用度、降低耦合度;
- 2、实现功能的复用;
- 3、业务隔离,跨团队开发代码控制和版本风险控制的实现
- 4、模块化对代码的封装性、合理性都有一定的要求,提升开发同学的设计能力。
它有这么多的优点,并且都一一击中了我们之前项目架构的痛点,我们有不用的道理么?
三、如何搭建组件化架构
在我们开始按照组件化的架构去重构我们的项目之前,我们先要了解如何利用IDE去搭建组件化架构。
现在Android官方在强推Android Studio,Eclipse已经被逐步弃用,那么我们也紧跟步伐,基于Android Studio搭建组件化。
这一次的组件化架构是基于之前粗略的组件化方式,作出大量的优化调整,然后利用子工程与壳工程互相配合实现。
实现目标结构如下:
壳工程:单个,项目骨架配置相关
组件工程:多个,多个业务并存且互相独立。
库工程:多个,基础库、工具库、数据库、第三方框架库、网络库……
组件化的结构已经订好,那么我们就可以着手去搭建了。
1、新建项目
如果直接在之前的项目整理,可想而知,那绝对是个灾难,我们的计划是现在新项目上搭建好组件化架构,再把旧项目拆分重整到新项目里来。
打开Android Studio,选择 Start a new Android Studio project
后面选择一个空白的,然后下一步输入项目名MyModularization及相关信息,点击OK就可以了。
此时,Android Studio已经为我们添加了默认的Module: app,即我们的壳工程,它是一个app的骨架性配置,一些库的初始化之类的,没有什么代码实现。
2、为MyModularization添加组件
壳工程建立完毕,接下来通过添加Module的方式添加库组件和业务组件。
2.1、为MyModularization添加Library
右键点击MyModularization,选择new,再选择module
在新窗口上选择Android Library,next,再输入相关信息即可
2.2、为MyModularization添加业务组件
跟1.2差不多,只是在新窗口上选择的不是Android Library,而是Phone & Tablet Module,这样,我们为项目添加了两种类型的组件Library和Module,一般组件的类型就是这两种。
3、两种组件的区别
这两种组件(其实就是eclipse下的子工程)之间有什么区别呢,我们打开各自的build.gradle,看一下第一行
lib_base:
apply plugin: 'com.android.library'
module_login:
apply plugin: 'com.android.application'
这里,我们可以看出,库组件只仅仅是个库,不能单独运行,而业务组件则是application,能单独运行。
项目及组件添加完成后,我们还要做一些配置。
4、库模块的配置
如果,我们在添加的时候,已经选择了Library,IDE基本上已经帮我们配置完成,但是如果需要把一个非Library类型的组件修改为业务组件,则需要如下操作
4.1、配置apply plugin
把build.gradle中的apply plugin: ‘com.android.application’修改成apply plugin: ‘com.android.library’
4.2、调整manifest
把manifest中的代码作如下修改
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="yb.demo.modularization.lib_base" />
5、业务组件中的配置
因为组件化的其中一个优势,组件需要具备单独开发调试测试,故,作为整体的一部分时,组件是个Library,而开发过程中,组件是个Application,故配置如下:
5.1、添加调试开关
在project的build.gradle中添加是否Application的开关
ext {
isModule = true;
}
5.2、配置plugin类型
在build.gradle的顶部添加如下代码,读取开关,做相应的配置
if(rootProject.ext.isModule.toBoolean()){
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
5.3、添加applicationId
在build.gradle添加applicationId
android {
…………
defaultConfig {
if(rootProject.ext.isModule.toBoolean()){
applicationId "com.example.login"
}
…………
}
5.4、添加两种AndroidManifest.xml
在Module中添加一个/src/main/module的文件夹,并在module创建AndroidManifest.xml*配置文件,用来在模块开发时能够单独打包,配置如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.login">
<application
android:name=".debug.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
…………
</manifest>
在module包下添加文件/src/main/java/包下/debug/**MyApplication.java **,做一些调试的开发相关的实现。
而作为 Library 的配置文件 /src/main/AndroidManifest.xml的配置调整如下
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.login">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.login.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
</activity>
…………
</manifest>
5.5、配置选择AndroidManifest
前面一步已经让module拥有了两个配置文件,接下来就是根据前面添加的开关,判断使用哪个配置文件。
还是在build.gradle文件作出以下调整:
android {
…………
sourceSets {
…………
main {
if (rootProject.ext.isModule.toBoolean()) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
//集成开发模式下排除debug文件夹中的所有Java文件
java {
exclude 'debug/**'
}
}
}
…………
}
…………
}
5.6、依赖库工程
业务组件需要依赖对应的库工程才能,进行相关功能的实现。
在业务的build.gradle文件做如下配置:
dependencies {
……
api project(':lib_base')
}
5.7、让业务工程具备独立调试的能力
在业务的build.gradle文件做如下配置:
dependencies {
……
if(rootProject.ext.isModule.toBoolean()){
// andorid text
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
api project(':lib_base')
}
6、壳工程依赖库工程及业务工程
在app的build.gradle文件做如下配置:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//旧版本Android Studio配置关键字
// compile project(':module_login');
// compile project(':lib_base');
api project(':module_login');
api project(':lib_base');
}
ok,这样我们组件化基本架构就搭建完毕了,在壳工程与业务工程、库工程关联完毕后,就可以作为一个项目来进行开发了。
当然,这里只是举例了lib_base和module_login的相关配置,其他的子工程也无非这两种类型的其中一种,配置都是一样的,他们统统做相应的配置即可。
四、利用ARouter降耦合
进过上面的配置,我们的组件化基本架构已经完成,这里还有个问题存在,就是每个子模块之间不能直接调用,那么我们怎么进行模块之间的跳转与数据交流呢。
首先想到的解决的办法有两个,隐式跳转和反射,但是,这两种方式一种实现维护麻烦,一种对性能会有影响,都不是理想的解决办法,此时类似ARouter和ActivityRouter等的路由框架就派上用场了。
我们选择的是阿里的ARouter路由框架,,我们先来简单了解一下ARouter的原理
路由框架会扫描所有添加@Route注解的Activity类,然后将Route注解中的path地址和Activity.class文件建立映射关系并且保存到它自己生成的java文件中,以实现跳转。
详解ARouter路由框架原理。(貌似也还没有写,加下文提到的“纵向横向重构项目”,后面会抽时间完成)
接下来,我们就可以把ARouter运用到我们的组件化中了。
1、配置引用ARouter
我们一般把公用第三方框架放在lib_base中,故在lib_base 的build.gradle文件做如下配置:
dependencies {
………………
// ARouter
// 替换成最新版本, 需要注意的是api
// 要与compiler匹配使用,均使用最新版可以保证兼容
api 'com.alibaba:arouter-api:1.4.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
}
2、子工程引入路由器
在需要对外提供跳转的子工程中添加路由器,在对应的module的build.gradle文件做如下配置:
if(rootProject.ext.isModule.toBoolean()){
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
………………
defaultConfig {
………………
// ARouter
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
………………
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
if(rootProject.ext.isModule.toBoolean()){
// andorid text
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
// ARouter
annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
api project(':lib_base')
}
3、添加注解
在需要夸module跳转的activity中添加注解:
// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/com/example/login/MainActivity")
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
………………
}
4、使用路由器夸module跳转
注解添加后,我们就可以通过path实现夸module跳转了。在需要startActivity的地方做调整:
//弃用
private void showActivity(Class<?> cls){
Intent show = new Intent(this, cls);
startActivity(show);
}
private void showActivity(String path){
// 应用内简单的跳转(通过URL跳转在'进阶用法'中),还有带参跳转,以后在单独见ARouter的时候一起讲。
ARouter.getInstance().build(path).navigation();
}
5、Kotlin项目中的配置方式
// 可以参考 module-kotlin 模块中的写法
apply plugin: 'kotlin-kapt'
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
compile 'com.alibaba:arouter-api:x.x.x'
kapt 'com.alibaba:arouter-compiler:x.x.x'
...
}
OK,这样就可以把ARouter运用到我们的架构中了,为架构降耦作贡献。
五、组件化思维重构项目
以上,我们就搭起了组件化的架构,接下来才是头疼的事情。
重构的关键是要有组件化的思维,我们首先要做的是,理清冗余杂乱重复的功能模块、工具具模块以及纷繁错乱的业务。
由于篇幅的原因,我们这里只讲搭建组件化架构,不讲重构,如想了解我们是怎么重构的,另见Android组件化二:纵向横向重构项目, 貌似还没写!!;
既然不讲过程,那么就直接来结果吧
组件的划分:
组件化结构:
然后,既然讨论清楚,也就是设计完毕啦,接下来就可以进入-炼狱了,你懂的。
六、结语
虽然重构的过程是痛苦的,甚至有时候会觉得生不如死,但是,我们一定要坚持下来,因为,坚持到最后,项目组件化起来了,大大降低了项目维护的成本,各种爽各种嗨。
本文详细的介绍了,如何搭建一个简单易懂的组件化架构,如果你们的项目还没有接入组件化架构,不妨参考一下。
能力有限,如有哪里描述不当或者不清楚的地方,望各位大大指出,谢谢。