组件化就是将我们的APP拆分成很多个模块,每个模块可以单独运行,以便于开发维护和测试,组件化中必不可少的是Gradle的配置,Gradle中使用的是Groovy语言,Groovy也是JVM语言的一种,如果你熟悉kotlin,那么学习Groovy将更容易,这也得益于kotlin结合了各大语言的优点,引入了很多最新、最流行的概念
一、Groovy简单上手
在AS中新建一个module,并在生成的Gradle中练习Groovy的基础语法
1.定义变量
Groovy中变量的定义需使用def关键字,而且不需要强制指定类型
代码语言:javascript复制task testGroovy() {
def i = 1
println(i)
}
2.定义类
Groovy兼容Java,定义类就和Java一样了,类中成员变量会隐式生成get/set方法,最后观察打印方法的调用,在Groovy中方法调用可以省略"()"
代码语言:javascript复制task testGroovy() {
Project p = new Project("groovy", "1.0.0", "jvm")
// 打印版本号
println p.version
}
class Project {
String name
String version
String group
Project(String name, String version, String group) {
this.name = name
this.version = version
this.group = group
}
}
3.字符串
Groovy有三种定义字符串的方式
- 单引号
task testGroovy2() {
def s = 'hello'
println s
}
- 双引号,和kotlin一样,可以使用$符取变量值
task testGroovy2() {
def s1 = 'hello'
def s2 = "$s1 groovy"
println s2
}
- 三个单引号,可以换行
task testGroovy3() {
def s = '''hello
groovy
is
simple
'''
println s
}
4.集合
- list
定义list时使用 "[ ]"
代码语言:javascript复制task testGroovy4() {
def list = ['one', 'two', 'three']
println list[1]
}
使用 "<<" 添加一个元素
代码语言:javascript复制task testGroovy4() {
def list = ['one', 'two', 'three']
list << 'four'
println list[3]
}
- map
map使用 ":" 隔开,区别key和value
代码语言:javascript复制task testGroovy5() {
def map = ['one': 1, 'two': 2, 'three': 3]
map << ['four': 4]
println map['four']
println map.four
}
5.闭包
熟悉kotlin的话,理解起来就简单了,在使用上就是lambda表达式表示的变量,也就是函数类型变量
代码语言:javascript复制task testGroovy6() {
def print1 = { message ->
println "$message end"
}
print1 "hello"
}
二、Gradle
Gradle主要分为两个基本概念:项目(Project)与任务(Task),可以用线程和方法的关系来理解,一个项目可以包含多个任务,并且可以手动配置多个任务的调用链
1.Project
build.gradle文件在构建时相当于一个Project,又称为组件,常用的方法有:apply、dependencies、repositories、task Project自带属性为:group、name、version 还可以使用ext、gradle.properties来定义属性
2.Task
任务是最小的工作单元,可以定义任务依赖于其他任务,调用序列和执行条件。常用方法有dependsOn、doFirst、doLast 如下面例子,执行doTwo任务时,会先执行doOne任务
代码语言:javascript复制task doOne() {
println "doOne"
}
task doTwo() {
dependsOn doOne
println "doTwo"
}
结果:
doFrist、doLast则可以指定任务中代码块的执行顺序:
代码语言:javascript复制task doOne() {
println "doOne"
}
task doTwo() {
dependsOn doOne
println "doTwo"
doLast {
println "doLast"
}
doFirst {
println "doFirst"
}
}
结果:
三、组件化
随着app的迭代,业务越来越繁重,为了让业务可以分层,组件化出现了,经过基础组件的支撑,业务层组件可以单独运行,以便于新功能的开发于测试
1.新建业务module_a
业务module是可以单独运行的,所以它的gradle使用的是com.android.application插件
将创建项目时的默认的app module作为Application,最后完整的app还是得通过该module来编译,但此时并不能将module_a引入到app module中,找不到module_a
2.修改业务module_a的gradle配置
如果需要module_a能够被引入,需要使用com.android.library插件,但是它不能和com.android.application插件同时使用,怎么办呢?答案是使用变量动态配置gradle
代码语言:javascript复制def isModule = true
if (isModule) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
apply plugin: 'kotlin-android'
manifest文件也是需要两份,一份作为application的,一份作为library的
src/main/AndroidManifest.xml:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.aruba.libmodule_a">
<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/Theme.ARouterApplication">
<activity
android:name=".ModuleAActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
src/main/manifest/AndroidManifest.xml:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.aruba.libmodule_a">
<application>
<activity android:name=".ModuleAActivity" />
</application>
</manifest>
在gradle中为他们配置,根据变量加载不同的manifest文件:
代码语言:javascript复制android {
compileSdk 31
defaultConfig {
if (!isModule) {// application才有id
applicationId "com.aruba.libmodule_a"
}
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
// 根据变量配置不同manifest
sourceSets {
main {
if (isModule) {
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
3.依赖基础组件实现module_a组件跳转到app组件
现在我们可以在app module主界面中跳转到module_a的Activity了
代码语言:javascript复制class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun toModule(view: android.view.View) {
startActivity(Intent(this, ModuleAActivity::class.java))
}
}
但是moudle_a的Activity并不能跳转到MainActivity,因为module_a并没有引入app组件
解决方法:
3.1 在基础base组件中缓存各个Activity的类,利用缓存获取需要跳转的类
代码语言:javascript复制object ActivityCache {
val ActivityClzByName: MutableMap<String, Class<out Activity>> = mutableMapOf()
}
3.2 然后在module_a中依赖该base组件
3.3 最后在Application中将所有Activity的类放入缓存
代码语言:javascript复制class App : Application() {
override fun onCreate() {
super.onCreate()
ActivityCache.ActivityClzByName["main"] = MainActivity::class.java
ActivityCache.ActivityClzByName["module_a"] = ModuleAActivity::class.java
}
}
3.4 在ModuleAActivity中利用缓存map跳转
代码语言:javascript复制class ModuleAActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_module_aactivity)
}
fun toMain(view: android.view.View) {
startActivity(Intent(this, ActivityCache.ActivityClzByName["main"]))
}
}
最后效果:
4.单独运行moudle_a
改下moudle_a中gradle的变量值,就可以单独运行moudle_a了