Android组件化实现、项目架构思想、组件化架构优化

3 篇文章 0 订阅
1 篇文章 0 订阅

首先我们需要理解,组件化是一种对完整Android项目模块进行分层的一种架构方式,如果了解过模块化的同学会发现,组件化和模块化的本质上并没有什么不同。废话不多说,接下来我们直接带着大家一起进行基本的组建化搭建,之后我们还会对架构进行整体优化

组件化的基本实现

第一步,进行项目分层

通常我们可以将组建化架构的Android项目分为三或四层,如下图

//1、分层划分:3-4层
//- app(application) 项目入口
//- business(application) 业务层,项目的一些基本业务都在这
//- core(application) 核心层,包含一些公用功能组件,例如音视频播放器等
//- library(library) 依赖库层,包含一些项目中用到的依赖库

然后,我们把项目创建为上述想要的架构方式,创建出所需要的application和library层级

创建application module

创建library module

创建完成后的项目架构

第二步,属性拓展和初始化

由于模块化可以对不同的application module进行单独调试,所以我们每个模块的灵活配置就尤为重要,因此我们需要对项目添加一些扩展属性

如上图,我们添加了common.gradle文件,添加了isModuleDebug用来判断当前模块是否需要单独调试,如果isModuleDebug为true就表示该模块可以单独运行,然后我们可以在所需要判断的模块build.gradle中使用apply from的方式将common.gradle使用进行引用,然后我们在对当前模块的isModuleDebug进行判断

之后,我们也可以对ApplicationId进行相应的判断了 (只有单独调试模式时,module才需要applicationId)

 与此同时,我们也需要对项目的一些初始化进行准备,对app主入口的Application进行编写和配置,我们可以在这里做一些项目的初始化配置,如下两张图

编写MyApplication继承Application

在模块中配置MyApplication

至此,我们的项目组件化的基本架构就算已经完成,接下来我们仍然需要对架构进行进一步的优化

组件化架构优化

第一步,清单文件的统一化管理

由于我们在组件化开发中,application是需要随时变更为library的,因此,我们也需要对清单文件(AndroidManifest)文件进行统一管理

首先,我们需要另外创建一个AndroidManifest.xml作为调试时使用,通常直接把原有的清单文件复制过来即可

然后,我们可以把原来的清单文件去除掉所有视图相关的配置,这样这个清单文件就可以作为该模块为library时编译使用,如下图

我们还需要把相应模块的这两个清单文件通过build.gradle文件中android内部使用sourceSets{

main{}}语法进行配置,同时判断是否为调试模式,如下图

最后,我们别忘了我们还需要在app模块下进行引用,在app下的build.gradle也通过apply将common.gradle引用进来

判断是否调试,如果是直接进行依赖即可 

第二步,版本信息和依赖的统一管理

以上我们虽然实现了组建化的架构,但是版本设置不够统一化,会导致产出多余的配置信息。因此,我们需要对每个模块的版本信息和依赖进行统一化的管理配置

首先我们可以把模块所需要的版本信息转移到common.gradle中

相应的一些配置信息也转移到common.gradle中(图中为转移前)

最后把相应的依赖也转移到common.gradle中

至此,所有的模块都只留下,其余部分可以注释或者删除(根据自身需求)

apply from: "${rootProject.rootDir}/common.gradle"

我们所有的module就只剩下了common.gradle的引用,接下来我们会把所有的上述信息都在common.gradle中进行添加

正式开始迁移,接下来都是基于common.gradle扩展project.ext中添加的信息,我们先将app模块的版本信息迁移过来,如下图,等同于创建了一些常量

之后,创建一个名叫libs的map集合,将所有依赖项进行统一维护

接下来是重头戏,我们将针对application类型的module设置创建一个名叫setAppDefaultConfig的闭包(可以理解为方法的一种,可以传参)将所需要的插件、依赖库、版本信息、SourceSets(找到对应的清单文件)等信息配置到项目中

//专门来设置application module
    setAppDefaultConfig = {
        project ->
            project.apply plugin: 'com.android.application'
            project.apply plugin: 'org.jetbrains.kotlin.android'
            setAndroidConfig project.android
            setDependencies project.dependencies
    }

    setAndroidConfig = {
        android ->
            android.compileSdk = project.compileSdk

            android.defaultConfig {
                minSdk project.minSdk
                targetSdk project.targetSdk
                versionCode project.versionCode
                versionName project.versionName

                //如果是非主app并且在调试模式下的模块,给applicationId添加一个后缀,防止applicationId冲突的问题
                if (project.name == 'app') {
                    applicationId project.applicationId
                } else if (project.isModuleDebug) {
                    applicationId project.applicationId
                    applicationIdSuffix = project.name
                    //举个例子,添加后这句后,若main模块单独编译,则会在applicationId后面添加main
                }

                //ARooter的额外配置
                javaCompileOptions {
                    annotationProcessorOptions {
                        //添加路由每个模块的名称,最好用+=来添加参数,防止参数和其他框架同名的问题
                        arguments += [AROUTER_MODULE_NAME: project.getName()]
                    }
                }
            }
            android.buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles android.getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                }
            }
            android.compileOptions {
                sourceCompatibility JavaVersion.VERSION_1_8
                targetCompatibility JavaVersion.VERSION_1_8
            }
            android.kotlinOptions {
                jvmTarget = '1.8'
            }

            //同样把sourceSets也转移到这里
            android.sourceSets {
                main {
                    if (project.name != 'app' && project.isModuleDebug) {
                        //以后创建application也需要创建debug下的AndroidManifest文件(需要制定开发的规范性)
                        manifest.srcFile "src/debug/AndroidManifest.xml"
                    } else {
                        manifest.srcFile "src/main/AndroidManifest.xml"
                    }
                }
            }
    }

 同时,我们也将针对library类型的module设置创建一个名叫setLibsDefaultConfig的闭包

    //专门来设置library module
    setLibsDefaultConfig = {
        project ->
            project.apply plugin: 'com.android.library'
            project.apply plugin: 'org.jetbrains.kotlin.android'
            setAndroidConfig project.android
            setDependencies project.dependencies
    }

我们会发现,上述的两个闭包同时还调用了 setDependencies这个闭包,它将提供项目的共有依赖项,我们也把它创建出来,如下代码

//设置依赖
    setDependencies = {
        dependencies ->
//            implementation project(':business:main')
//            implementation project(':business:login')
            //这里将delegate代理设置为dependencies这样如下依赖就可以在dependencies中寻找了,否则会找不到
            delegate = dependencies
            /**
             * 这里会遍历已经声明的map集合,然后分别进行依赖。
             * 注意:这里会出现多个module同时依赖的情况,单实际单独调试时本就已经节约了百分之80的时间,而且gradle会把依赖除重,所以不用担心
             */
            project.libs.each {
                key, value -> implementation value
            }
            project.apts.each {
                key, value -> annotationProcessor value
            }
            //可以用如下方式直接依赖,也可以通过上述声明方式更便捷的管理依赖
//            implementation 'androidx.core:core-ktx:1.7.0'
//            implementation 'androidx.appcompat:appcompat:1.3.0'
//            implementation 'com.google.android.material:material:1.4.0'
//            implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
            //独立的依赖方式可以放在这里
            androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
            //只有module为app主入口且非单独调试模式时进行依赖
            if (project.name == 'app' && !project.isModuleDebug) {
                implementation project(':business:main')
                implementation project(':business:login')
            }
    }

 最后,我们在project.ext扩展的外部添加setAppDefaultConfig闭包和setLibsDefaultConfig闭包的调用

if (project.name == 'app' || isModuleDebug) {
    //除了app,还有组建单独调试的情况,此类情况直接调用setAppDefaultConfig
    project.setAppDefaultConfig project
} else {
    project.setLibsDefaultConfig project
}

至此,我们的gradle整体迁移就算完成了 ,common.gradle完整代码如下

//添加项目的属性扩展,这里主要是作为公用的gradle处理
project.ext {
    //当前Module是否单独调试 false表示不单独调试
    isModuleDebug = true

    applicationId = "com.componential.demo"
    minSdk = 21
    targetSdk = 32
    versionCode = 1
    versionName = "1.0"
    compileSdk = 32

    //对所有依赖项进行统一管理 key简写即可 value是实际依赖项的全名
    libs = [
            "ktx"             : "androidx.core:core-ktx:1.7.0",
            "appcompat"       : "androidx.appcompat:appcompat:1.3.0",
            "material"        : "com.google.android.material:material:1.4.0",
            "constraintlayout": "androidx.constraintlayout:constraintlayout:2.0.4",
    ]
    //把ARooter也依赖进来
    apts = [
            "ARouterCompiler": "com.alibaba:arouter-compiler:1.5.2",
    ]
    //专门来设置application module
    setAppDefaultConfig = {
        project ->
            project.apply plugin: 'com.android.application'
            project.apply plugin: 'org.jetbrains.kotlin.android'
            setAndroidConfig project.android
            setDependencies project.dependencies
    }

    setAndroidConfig = {
        android ->
            android.compileSdk = project.compileSdk

            android.defaultConfig {
                minSdk project.minSdk
                targetSdk project.targetSdk
                versionCode project.versionCode
                versionName project.versionName

                //如果是非主app并且在调试模式下的模块,给applicationId添加一个后缀,防止applicationId冲突的问题
                if (project.name == 'app') {
                    applicationId project.applicationId
                } else if (project.isModuleDebug) {
                    applicationId project.applicationId
                    applicationIdSuffix = project.name
                    //举个例子,添加后这句后,若main模块单独编译,则会在applicationId后面添加main
                }

                //ARooter的额外配置
                javaCompileOptions {
                    annotationProcessorOptions {
                        //添加路由每个模块的名称,最好用+=来添加参数,防止参数和其他框架同名的问题
                        arguments += [AROUTER_MODULE_NAME: project.getName()]
                    }
                }
            }
            android.buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles android.getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                }
            }
            android.compileOptions {
                sourceCompatibility JavaVersion.VERSION_1_8
                targetCompatibility JavaVersion.VERSION_1_8
            }
            android.kotlinOptions {
                jvmTarget = '1.8'
            }

            //同样把sourceSets也转移到这里
            android.sourceSets {
                main {
                    if (project.name != 'app' && project.isModuleDebug) {
                        //以后创建application也需要创建debug下的AndroidManifest文件(需要制定开发的规范性)
                        manifest.srcFile "src/debug/AndroidManifest.xml"
                    } else {
                        manifest.srcFile "src/main/AndroidManifest.xml"
                    }
                }
            }
    }
    //专门来设置library module
    setLibsDefaultConfig = {
        project ->
            project.apply plugin: 'com.android.library'
            project.apply plugin: 'org.jetbrains.kotlin.android'
            setAndroidConfig project.android
            setDependencies project.dependencies
    }

    //设置依赖
    setDependencies = {
        dependencies ->
//            implementation project(':business:main')
//            implementation project(':business:login')
            //这里将delegate代理设置为dependencies这样如下依赖就可以在dependencies中寻找了,否则会找不到
            delegate = dependencies
            /**
             * 这里会遍历已经声明的map集合,然后分别进行依赖。
             * 注意:这里会出现多个module同时依赖的情况,单实际单独调试时本就已经节约了百分之80的时间,而且gradle会把依赖除重,所以不用担心
             */
            project.libs.each {
                key, value -> implementation value
            }
            project.apts.each {
                key, value -> annotationProcessor value
            }
            //可以用如下方式直接依赖,也可以通过上述声明方式更便捷的管理依赖
//            implementation 'androidx.core:core-ktx:1.7.0'
//            implementation 'androidx.appcompat:appcompat:1.3.0'
//            implementation 'com.google.android.material:material:1.4.0'
//            implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
            //独立的依赖方式可以放在这里
            androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
            //只有module为app主入口且非单独调试模式时进行依赖
            if (project.name == 'app' && !project.isModuleDebug) {
                implementation project(':business:main')
                implementation project(':business:login')
            }
    }
}

if (project.name == 'app' || isModuleDebug) {
    //除了app,还有组建单独调试的情况,此类情况直接调用setAppDefaultConfig
    project.setAppDefaultConfig project
} else {
    project.setLibsDefaultConfig project
}

第三步,实现组建间通信

最后的最后,由于组建间本身是没有通信手段的,我们还需要依赖一些手段来实现组建化通信。目前市面上有很多框架支持组建间通信,这里我们使用较为主流的阿里开发的ARooter框架实现组建间的通信,ARooter是一个阿里推出的路由引擎,是一个路由框架,并不是完整的组件化方案,可作为组件化架构的通信引擎,接下来我们直接在项目中依赖ARooter

//把ARooter也依赖进来
    apts = [
            "ARouterCompiler": "com.alibaba:arouter-compiler:1.5.2",
    ]

如上代码,我们先创建名叫apts的map的集合把ARooter的依赖存放进来,咱们使用当前最新的版本1.5.2,然后还需要通过each的方式遍历map集合,通过如下方式将arouter-api也依赖进来

最后,我们还需要添加一个ARouter的必添参数,直接配置在setAndroidConfig这个闭包中即可

至此,我们便完成了ARouter配置,并且完成了组建化从通信到架构的完整搭建,可以正式进入开发阶段啦,之后还可以继续对项目进行优化调整。

同时我们仍需注意的是,由于我们在上面在SourceSets中给application module设置了debug目录下的清单文件,因此,在今后的开发过程中需要针对开发人员制定相应规则,在每次创建新的module时需要在对应debug目录下创建清单文件,并把原有的清单文件改造成library所需的结构

额外拓展

本篇内容我们虽然已经做了完整的依赖和配置整合,但偶尔我们也会需要一个单独模块的定制化,因此我们仍然可以在任意模块的build.gradle中单独做配置,这样也进一步提升了项目的灵活性

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值