Android--Groovy语法、组件化架构简析

2021-12-06 17:40:25 浏览数 (1)

组件化就是将我们的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有三种定义字符串的方式

  • 单引号
代码语言:javascript复制
task testGroovy2() {
    def s = 'hello'
    println s
}
  • 双引号,和kotlin一样,可以使用$符取变量值
代码语言:javascript复制
task testGroovy2() {
    def s1 = 'hello'

    def s2 = "$s1 groovy"
    println s2
}
  • 三个单引号,可以换行
代码语言:javascript复制
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了

麻烦的是每次使用需要手动改变变量,下篇将利用ARouter来实现组件化

0 人点赞