Android-架构-组件化开发(一):项目搭建

组件化的优点

  • 编译速度:可以按需测试单一模块,因为每一个模块都可以当做一个application。
  • 超级解耦:极度的降低了模块之前的耦合,便于后期维护。
  • 功能重用:某一块的功能在另外的组件化项目中使用只需要单独依赖这一模块即可,例如login模块。
  • 便于团队开发:适用于大团队分模块各自开发,更好地团队协作,提升工作效率。

组件化需要注意的点

  • 要注意包名和资源文件名的冲突
  • Gradle中版本号的统一管理
  • 组件在ApplicationLibrary之间如何做到随意切换
  • AndroidManifest文件的区分
  • Library不能在Gradle文件中有applicationId

项目搭建

  1. 项目中新建模块(membercart

注意:要选择Application,不要选择Module

项目结构

各个模块都可直接运行,因为都是Application

  1. 编写common.gradle:统一整个项目各个模块的gradle、依赖库版本以及配置,放在项目根目录,与根build.gradle平级
apply plugin: "com.android.application"
apply plugin: "kotlin-android"

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0.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
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
}

注意:common.gradle中不要添加applicationId

  1. 各个模块apply from: "../common.gradle",删除通用配置,保留独有配置
apply from: "../common.gradle"

android {
    defaultConfig {
        applicationId "com.zt.member"
    }
}

dependencies {
}

注意:applicationId添加到各自模块中,因为每个模块都是唯一的

  1. 使组件在ApplicationLibrary之间随意切换,其实就是apply plugin: "com.android.application"还是apply plugin: "com.android.library"
    编写config.gradle,定义一个开关isApplication,也放在跟目录
ext {
    android = [
            // true:所有组件均为application;false:所有组件均为library
            isApplication: true
    ]
}

切换只需要修改isApplication的值即可。

  1. 在根build.gradle中引用config.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "config.gradle"
buildscript {
    ...
}

allprojects {
    ...
}
  1. 修改common.gradle
// app模块一定为application,其他模块参照配置
if (project.getName() == "app" || rootProject.ext.android.isApplication) {
    apply plugin: "com.android.application"
} else {
    apply plugin: "com.android.library"
}
apply plugin: "kotlin-android"

android {
    ...
}

dependencies {
    ...
}

到此为止,你sync整个项目,应该是可以编过,并且各个模块也是可以单独编译成 App 的。
但是,毕竟整个项目还是一个完整的 App ,那除了app模块是Application外,其他模块应该都是Library,那就修改isApplication: false

  1. 修改各个模块的gradle文件,因为如果是Library的话,是不可以存在applicationId的。
apply from: "../common.gradle"

android {
    defaultConfig {
        if (rootProject.ext.android.isApplication) {
            applicationId "com.zt.member"
        }
    }
}

dependencies {
}

既然是Library,那需要集成到app模块中

  1. app依赖其他组件,记得也要加上判断
apply from: "../common.gradle"

android {
    defaultConfig {
        applicationId "com.zt.componentdemo"
    }
}

dependencies {
    // 只有当模块为Library时,才可以进行依赖
    if (!rootProject.ext.android.isApplication) {
        implementation project(path: ":member")
        implementation project(path: ":cart")
    }
}
  1. Androidmanifest文件的区分,需要使得当组件在ApplicationLibrary下加载不同的Androidmanifest.
    在模块中新建manifest文件夹,路径:src/main/manifest,复制一份Androidmanifest.xml到该文件夹,并修改成Library适用的。
    member 模块

src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zt.member">

    <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.ComponentDemo">
        <activity android:name=".MemberActivity">
            <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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zt.member">

    <application>
        <activity android:name=".MemberActivity" />
    </application>

</manifest>
  1. 修改common.gradle,添加sourceSets,配置加载不同AndroidManifest文件。
android {
    ...
    sourceSets {
        main {
            // 指定AndroidManifest文件路径
            if (project.getName() == "app" || rootProject.ext.android.isApplication) {
                manifest.srcFile "src/main/AndroidManifest.xml"
            } else {
                manifest.srcFile "src/main/manifest/AndroidManifest.xml"
            }
        }
    }
}
  1. 如果组件是Application的话,每个模块都需要一个xxxApplication,但是当组件变成Library 的话,xxxApplication就不需要了,也不需要打包到最终的app中,那就需要进行配置了,思路与AndroidManifest文件类似
    在模块中新建debug文件夹(名字自取),并添加包名,随后将临时代码文件添加到该目录下,如:xxxApplication.java,xxxUtils.java等等
    member 模块

    并在common.gradle中的sourceSets里添加配置
sourceSets {
    main {
        // 指定AndroidManifest文件路径
        if (project.getName() == "app" || rootProject.ext.android.isApplication) {
            manifest.srcFile "src/main/AndroidManifest.xml"
            java.srcDirs "src/main/debug"
        } else {
            manifest.srcFile "src/main/manifest/AndroidManifest.xml"
        }
    }
}

注意:请注意debug前面图标的颜色,如果是蓝色(isApplication: true),则表示这里面的java文件会被编译进app中,如果是灰色(isApplication: false),则表示不会。

  1. java代码中区分模块类型
    首先现在common.gradle中使用buildConfigField添加参数
defaultConfig {
    minSdkVersion 21
    targetSdkVersion 30
    versionCode 1
    versionName "1.0.0"
    // 添加参数,便于在java文件中使用 buildConfigField type name value
    buildConfigField "boolean", "isApplication", "$rootProject.ext.android.isApplication"
}

Make Project之后,会在各个模块的BuildConfig文件中生成出该字段,这样代码中就可以使用了。

BuildConfig 路径

  1. 最后,可以再建立个common模块,这里新建选择Library即可
    common 模块

别忘了在common.gradle中添加依赖

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "androidx.core:core-ktx:1.3.2"
    implementation "androidx.appcompat:appcompat:1.2.0"
    implementation "com.google.android.material:material:1.2.1"
    implementation "androidx.constraintlayout:constraintlayout:2.0.2"

    implementation project (path: ":common")
}

整个项目已搭建完成,组件化开发中还有最重要的两个点,路由和通信,会在后面两篇文章中进行分享。

github地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341