Android组件化实践

1 目的以及组件化与模块化区分

参考自:分析组件化与模块化之间的区别
组件化:把重复使用的代码拆分成一个个组件,提供给功能使用
模块化:按照项目功能需求,将代码拆分成一个个模块。
区别:组件化是为了重用而拆分模块, 模块化是为了业务分离而拆分模块

安卓组件化示意图:

在开发阶段组件1、组件2、组件3都可以单独作为一个应用独立运行,上线时组件1、组件2、组件3 + App壳 + 依赖的其他模块共同构成应用。

2 组件化具体操作

demo项目参考地址:

2.1 统一编译工具版本号以及依赖版本号

(1) 统一编译工具版本号
在项目根目录build.gradle中增加编译工具配置

...
ext {
    ...

    sdkID = [
            "compileSdkVersion": 30,
            "buildToolsVersion": "30.0.1",
            "minSdkVersion"    : 21,
            "targetSdkVersion" : 30,
    ]
}

在各模块中引用,如base模块的build.gradle引用(其他模块同理)

plugins {
    id 'com.android.library'
}

android {
    compileSdkVersion rootProject.ext.sdkID.compileSdkVersion
    buildToolsVersion rootProject.ext.sdkID.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.sdkID.minSdkVersion
        targetSdkVersion rootProject.ext.sdkID.targetSdkVersion
        versionCode 1
        versionName "1.0"
    }
    ...
}
...

(2) 统一项目依赖版本号
同样,在项目根目录build.gradle中增加依赖配置

...
ext {
    ...
    dependenciesID = [
            "appcompat"        : "androidx.appcompat:appcompat:1.2.0",
            "material"         : "com.google.android.material:material:1.2.1",
            "constraintlayout" : "androidx.constraintlayout:constraintlayout:2.0.4",
            "recyclerview"     : "androidx.recyclerview:recyclerview:1.1.0"
    ]
}

在各模块中引用,如base模块的build.gradle中如下引用:

...
dependencies {
    implementation rootProject.ext.dependenciesID.appcompat
    implementation rootProject.ext.dependenciesID.material
    implementation rootProject.ext.dependenciesID.constraintlayout
}

2.2 组件化配置

(1) 在根目录build.gradle中增加变量isRunTotalApp,用于指示各组件是否作为library

ext {
    ...
    //true 表示各组件共同构成一个应用, false表示各组件可单独成为应用
    isRunTotalApp = false
}

(2)在App壳中配置对module1的引用

...
dependencies {
    ...
    if (isRunTotalApp) {
        implementation project(":module1")
    }
}
...

(3)在组件module1对应的build.gradle中进行如下配置, 指定应用是否作为library、应用id、应用对应的AndroidManifest.xml文件

if (isRunTotalApp) {
    //整体作为一个应用时作为library
    apply plugin : "com.android.library"
} else {
    //单独运行时作为application
    apply plugin : "com.android.application"
}

android {
    compileSdkVersion rootProject.ext.sdkID.compileSdkVersion
    buildToolsVersion rootProject.ext.sdkID.buildToolsVersion

    defaultConfig {

        if (!isRunTotalApp) {
            //单独运行时指定应用id
            applicationId "com.example.module1"
        }


        minSdkVersion rootProject.ext.sdkID.minSdkVersion
        targetSdkVersion rootProject.ext.sdkID.targetSdkVersion
        versionCode 1
        versionName "1.0"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    sourceSets {
        main {
            if (!isRunTotalApp) {
                // 单独运行时指定AndroidManifest.xml文件
                manifest.srcFile 'src/main/test/AndroidManifest.xml'
            } else {
                // 作为一个组件时指定AndroidManifest.xml文件, 默认AndroidManifest.xml文件
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
}
...

单独运行时需要在main/test文件夹下创建AndroidManifest.xml文件,指定组件单独运行时的入口。
sync项目后即可在运行窗口中看到module1组件,需要时可以将isRunTotalApp设置为true运行整个项目。

3 组件间跳转: 使用ARouter使用组件间跳转

再次创建一个组件module2, 和module1配置一样,
通常而言, 可以通过隐式intent来实现组件间的跳转,还有就是通过路由的方式实现组件间跳转,如使用ARouter框架。
相关配置参考:ARouter一个组件化工程模板(kotlin,androidX,ARouter)androidX与support-v4包冲突问题

3.1 ARouter配置

1、在项目根目录下的build.gradle文件中增加ARouter依赖

...
ext {

    arouter_compiler_version = "1.5.1"
    arouter_api_version = "1.5.1"

    dependenciesID = [
            ...
            "arouterApi"      : "com.alibaba:arouter-api:${arouter_api_version}",
            "arouterCompiler" : "com.alibaba:arouter-compiler:${arouter_compiler_version}",
            ...
    ]

    sdkID = [
            "compileSdkVersion": 30,
            "buildToolsVersion": "30.0.1",
            "minSdkVersion"    : 21,
            "targetSdkVersion" : 30,
    ]

    //true 表示各组件共同构成一个应用, false表示各组件可单独成为应用
    isRunTotalApp = true
}

2、在app壳中配置ARouter的依赖以及需要的AROUTER_MODULE_NAME属性

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
    ...
}

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}

dependencies {

    ...

    implementation(rootProject.ext.dependenciesID.arouterApi)
    kapt rootProject.ext.dependenciesID.arouterCompiler

    ...

    if (isRunTotalApp) {
        implementation project(":module1")
        implementation project(":module2")
    }

}
...

在module1以及module2的build.gradle文件中同样配置。
3、在应用的Application中调用ARouter.init方法初始化ARouter。

package com.example.myapplication

import android.app.Application
import android.content.Context
import com.alibaba.android.arouter.launcher.ARouter

class BaseApplication : Application() {
    companion object {
        lateinit var context : Context
    }

    override fun onCreate() {
        super.onCreate()
        context = this
        ARouter.init(this)
    }
}

在需要启动的activity上增加注解,配置路径,用来进行跳转。

@Route(path = "/module2/MainModule2Activity", group = "module2")
public class MainModule2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        String from = getIntent().getStringExtra("from");
        Toast.makeText(this, String.valueOf(from), Toast.LENGTH_SHORT).show();
    }
}

使用如下代码即可实现跳转,并传递from数据:

ARouter.getInstance().build("/module2/MainModule2Activity")
                    .withString("from", "" + getClass().getName())
                    .navigation(this);

3.2 ARouter依赖的support包与androidX冲突问题

在gradle.properties中添加如下属性(参考自ARouter的一个issue以及AndroidX概览):

android.enableJetifier=true

3.3 demo下载

链接:https://gitee.com/2820174512/application

原文地址:https://www.cnblogs.com/wushengwuxi/p/14105817.html