安卓组件化架构设计

本文详细介绍了Android组件化开发的实践过程,包括Gradle语法的应用、组件化项目部署、子模块间的交互、注解处理器(APT)的使用以及动态隔离策略。通过创建自定义注解和注解处理器,实现了动态生成路由类文件,简化了模块间跳转和数据传递。此外,还探讨了组件化模式和集成化模式的切换,以及如何通过配置实现动态隔离,确保在组件化开发中的高效性和灵活性。

目录

写在前面

一、Gradle语法

二、组件化项目部署

2.1、组件化项目的意义

2.2、Phone Module和Android Library的区别

2.3、项目构建基础

2.4、集成化模式和组件化模式开发

2.5、动态隔离

三、子模块间交互

四、APT介绍与使用

4.1、APT简介

4.2、APT实战

五、APT高级用法JavaPoet

5.1、什么是JavaPoet

5.2、JavaPoet代码实战

六、组件化路由架构设计

6.1、配置arouter_api

6.2、配置common

6.3、APT生成路由Group和Path类文件

6.4、组件化APT生成路由动态参数类文件


写在前面

今天是1024程序猿节,首先祝各位猿猿们节日快乐,过节不加班!

今天思考再三之后,决定来写一篇Android组件化介绍的文章,说一说Android中的组件化如何使用,在这里我们先不使用阿里的ARouter,我们自己先实现一版功能简单的路由框架,下一篇再来使用业内比较知名的阿里的ARouter框架实现组件化。大家要明白什么是组件化,在我看来哈,组件化并不是一种需要你去怎样学习的技术,它实际上是对业务层面,具体到项目中就是项目架构上的解耦,将每个业务模块做成可拆分可集成化的组件,所以说组件化实际上就是基于模块化进行的。核心是模块的角色具有可转变的特性,集成化模式打包时各个业务组件是作为Android Library,打出来的只有一个完整的apk包,在单独调试模式的时候,每个业务组件都是作为单独的一个application存在,单独调试单独运行,互不干扰!

组件化非常重要,在中大型项目中基本上都会用到,我们首先来学习Gradle语法,因为它对于我们组件化环境的配置是十分重要的,gradle我们在项目中每天都能用到,见的很多了,但是很多人却不一定会写哈,下面就来总结一下Gradle语法相关的知识点。

一、Gradle语法

说明:由于本篇是介绍组件化架构而不是专门介绍Gradle语法的,所以这一部分并不会详细介绍具体的语法规则,而是结合实际应用来介绍项目中经常用到的一些规则或者是配置的使用。

Gradle简介

  • Gradle核心是基于Groovy脚本语言,Groovy脚本基于Java且拓展了Java。因此Gradle需要依赖JDK和Groovy库
  • 和ant、maven构建有区别,gradle是一种编程思想

Gradle语法简介

①、万法第一式:Hello World

我们在Build的Toggle View中打印"Hello Gradle",有两种方式:

点击右上角的同步操作,同步完成后下方Sync面板中就会输出结果了:

然后我们新建一个Android的Library,这里面肯定也有一个build.gradle文件吧,打开之后和app module下的build.gradle文件对比,你会发现这两者之间有很多东西都是一样的,那么此时你能想到什么?321,对喽,可以把公共的部分抽离出来嘛。

First:新建一个config.gradle用作公共的配置文件,这里举个栗子如下所示,关于一些语法已经做了注释:

//添加多个自定义属性,可以通过ext代码块
ext {

    //生产/开发环境(或者叫做正式/测试环境)
    //false:组件化模式(子模块可以独立运行)true:集成化模式(打包整个项目apk,子模块不可独立运行)
    isRelease = true

    //生产/开发环境域名地址
    hostUrl = [
            debug  : "http://www.111.com/debug/",
            release: "http://www.222.com/release/"
    ]

    //建立Map存储,对象名、key都可以自定义,groovy的语法糖非常灵活(字典)
    androidConfig = [
            compileSdkVersion: 29,
            buildToolsVersion: "29.0.2",
            minSdkVersion    : 14,
            targetSdkVersion : 29,
            versionCode      : 1,
            versionName      : "1.0"
    ]

    //ApplicationId(应用包名)
    appId = [
            app    : "com.jarchie.component",
            library: "com.jarchie.library"
    ]

    //依赖库版本:调用处的语法为:${keyName}
    depVersions = [
            appcompatVersion       : "1.2.0",
            consVersion: "2.0.1"
    ]

    //依赖库
    depConfig = [
            appcompat       : "androidx.appcompat:appcompat:${depVersions.appcompatVersion}",
            constraintlayout: "androidx.constraintlayout:constraintlayout:${depVersions.consVersion}"
    ]

}

Second:需要在工程的根目录下的build.gradle文件中引入咱们的公共配置文件:

//根目录下的build.gradle头部引入自定义config.gradle,相当于layout布局中加入include标签
apply from: "config.gradle"

Third:修改原来的app module下的build.gradle文件,在文件中使用我们的基础配置:

apply plugin: 'com.android.application'

//赋值与引用
def androidConfig = rootProject.ext.androidConfig
def appId = rootProject.ext.appId
def depConfig = rootProject.ext.depConfig

android {
    //上面定义了三个属性,分别指向公共配置config.gradle中定义的集合,然后根据集合中的key来获取对应的值
    compileSdkVersion androidConfig.compileSdkVersion
    buildToolsVersion androidConfig.buildToolsVersion
    defaultConfig {
        applicationId appId.app
        minSdkVersion androidConfig.minSdkVersion
        targetSdkVersion androidConfig.targetSdkVersion
        versionCode androidConfig.versionCode
        versionName androidConfig.versionName
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //gradle自带的这种方式实际上是一种简写
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    //它的标准写法应该是下面这种:
    implementation group:'androidx.appcompat',name:'appcompat',version:'1.2.0'

    //经过全局配置之后,依赖方式应该写成如下的格式
    implementation project(":library")
    implementation depConfig.appcompat
    implementation depConfig.constraintlayout

    //原理:实际上这种依赖的语法,本质相当于从Map中按照Key取对应的值,比如下面这样
    HashMap<String,String> map = new HashMap<>()
    implementation map.get("appcompat")

    //所以,实际上对于依赖第三方库最简洁的语法可以写成下面这种形式,遍历这个Map集合
    depConfig.each { k, v -> implementation v }
}

OK,经过上面的三个步骤就已经完成了对公共内容的抽离,然后可以按照同样的方法修改library moudule中的build.gradle文件中的内容。关于公共的配置相关的使用已经介绍的差不多了,但是别急还没完,因为你会发现config.gradle文件中定义的东西有两个还没用到,先说一下定义的isRelease,这个东西目前先放着因为暂时没啥好说的,就是一个属性,不过在下面说组件化的时候是十分重要的,它是用来识别是否是组件化以及决定module是组件还是单独的app的一个标识。

还有一个属性hostUrl我们也没有用到,那么想要用它我们就必须了解一个类BuildConfig,这个类的位置如下图所示:

它是存在于APK中的,你可以打包之后找到classes.dex文件,在这个文件中你就能看到它的存在。那么如果我们可以往这个类中添加一个属性,那是不是你就可以在任何地方调用你自定义的属性了,是不是非常方便,下面就来看一下该怎么添加自定义的属性呢?

如下所示:在build.gradle中的buildTypes中添加如下的两行代码,使用buildConfigField()这个方法来进行添加:

buildTypes {
        //public void buildConfigField(@NonNull String type,@NonNull String name,@NonNull String value){}
        //此方法接收三个非空参数:①确定值的类型;②指定key的名字;③传值(必须是String)
        //注意:不能在android根节点,只能在defaultConfig或者buildTypes节点下
        debug {
            buildConfigField("String", "DEBUG_URL", "\"${hostUrl.debug}\"")
        }
        release {
            buildConfigField("String", "RELEASE_URL", "\"${hostUrl.release}\"")
        }
    }

此时你再次进行同步就可以发现已经添加进去了,你当前是什么环境它就给你添加了哪个属性,比如我这里是Debug环境:

关于Gradle的语法部分就先介绍这么多,以上这些都是咱们这次能用到的,需要熟练掌握。

二、组件化项目部署

2.1、组件化项目的意义


组件化简单来说就是把一个非常完整的功能拆分成多个子模块,每个子模块又可以独立的编译和运行,也可以任意组合成为一个新的App

  • 开发需求:不相互依赖,可以相互交互、任意组合、高度解耦
  • 团队效率:分模块打包、测试,统一版本管理

2.2、Phone Module和Android Library的区别

①、Phone Module

  • 新建出可以独立运行的模块,可以看成是app,配置为:apply plugin:'com.android.application'
  • 它有applicationId
  • 切换为Android Library可以修改配置为:apply plugin:'com.android.library'

②、Android Library

  • 新建出Android库,不能独立运行,配置为:apply plugin:'com.android.library'
  • 没有applicationId
  • 切换为Phone Module可以修改配置为:apply plugin:'com.android.application'

2.3、项目构建基础

首先,新建一个空的工程,然后创建common公共基础库,接着分别创建四个module:home、product、order、personal,关于如何创建工程以及module,我这里就不详细说了,这些都是最基础最简单的,如果你不会请自行谷歌或百度。
然后,我们按照上面第一大部分介绍的Gradle相关的知识点,来创建config.gradle,并且在工程根目录中引入,然后依次修改每个module下的build.gradle文件,关于这一部分的操作步骤,在上面第一大点中已经详细列出了,可以回过头去翻翻看。
这里提几点组件化开发的规范:

  • app module:可不改,让它默认
  • order module:order_前缀(src类和res资源)
  • personal module:personal_前缀(src类和res资源)

简单点说就是尽量每个单独的module都使用modulename_xxx这种形式,比如:
 

2.4、集成化模式和组件化模式开发

关于集成化模式和组件化模式就要用到我们上面定义的isRelease这个属性了,还记得config.gradle中定义过这个东西嘛?

//生产/开发环境(或者叫做正式/测试环境)
//false:组件化模式(子模块可以独立运行)true:集成化模式(打包整个项目apk,子模块不可独立运行)
isRelease = true

它的出现就是为了方便我们去切换是使用的集成化模式还是组件化模式,当然每个module的build.gradle文件中也要做些修改:

首先是头部的module类型的配置,需要根据isRelease来灵活实现可控:

if (isRelease) { //如果是发布版本,各个模块都不能独立运行
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}

然后是android闭包下的defaultConfig节点下的applicationId属性,因为集成化模式下是没有这个属性的:

android {
    defaultConfig {
        if (!isRelease) { //如果是集成化模式,不能有applicationId
            applicationId appId.home //组件化模式独立运行时才可以有applicationId
        }
    }
}

然后依次修改每个module中的这几项配置即可,对于app module中的依赖项,也需要使用该属性进行判断:

    //如果是集成化模式,在发布版本时,各个模块都不能独立运行了
    if (isRelease){
        implementation project(':home')
        implementation project(':product')
        implementation project(':order')
        implementation project(':personal')
    }

配置完成之后,集成化模式开发和组件化模式开发项目结构对比:
  

2.5、动态隔离

组件化开发的临时代码,集成化打包时动态隔离:这句话是什么意思呢?比如当前项目每个module单独分给了项目组中的一个成员去做,每个人在开发的时候使用组件化模式单独编译运行时不可避免的会添加一些额外的测试类,而针对于这些额外的代码实际上在集成化模式下都是不需要的,它们无需合并到主工程中去,所以需要采用动态隔离技术在集成时将这部分代码剔除掉。

首先说几点规范:

①、Manifest.xml文件在单独运行时,我们在main文件夹下新建一个debug包,配置一份组件化模式下使用的清单文件:

②、在main/java/packageName目录下新建debug目录,将测试类写在该目录下:

③、在module的build.gradle文件中的android闭包下添加如下配置:

    //配置资源路径,方便测试环境,打包不集成到正式环境
    sourceSets{
        main{
            if (!isRelease){
                //如果是组件化模式,需要单独运行时
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            }else {
                //集成化模式,整个项目打包到apk
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java{
                    //release时debug目录下文件不需要合并到主工程
                    exclude '**/debug/**'
                }
            }
        }
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值