從模塊化到組件化
一、從模塊化到組件化
Android 應用項目 , 都存在一個應用模塊 ( Application Module ) , 在 build.gradle 構建腳本中 , 第一個插件配置 com.android.application , 表明 該 Module 編譯打包后的輸出是 APK 安裝包 ; 該項目可以直接運行 ;
plugins {
id 'com.android.application'
id 'kotlin-android'
}
如果在 build.gradle 配置的是 com.android.library 插件 , 那么 編譯 Module 打包后輸出的是 aar 依賴庫 ; 該項目不能直接運行 ;
plugins {
id 'com.android.library'
id 'kotlin-android'
}
模塊化 :
隨著應用業(yè)務增加 , 功能變得越來越復雜 , 不能將所有的功能放在一個 Application 模塊中 ; 大型項目的開發(fā)不能只有一個 Module , 大多數(shù)情況下 , Android 工程中 , 除了有一個 Application 模塊外 , 還有若干 Library 模塊提供給應用模塊引用 ; 應用中還可能存在一個基礎的 SDK 依賴庫 , 提供給 Library 模塊引用 , Application 再引用這些 Library 模塊 ;
模塊化的缺點 :
Library 模塊中實現(xiàn)了一個功能 , 如果要運行的話 , 需要借助 Application 模塊 , 這就需要將整個項目全部編譯一遍 , 如果項目有幾百個模塊 , 調(diào)試運行就很困難 ;
單個開發(fā)者可能只負責幾個模塊 , 還涉及了與其它模塊開發(fā)人者進行協(xié)作 ;
組件化 :
組件化是在模塊化的基礎上 , 可以 動態(tài)切換其模塊類型 , 將 Library 模塊切換成 Application 模塊 , 這樣獨立的模塊可以直接運行 ; 在進行 組件模式 開發(fā)時 , 將其變成 Application 模塊 , 在 集成模式 開發(fā)時 , 將其變成 Library 模塊 ; 組件開發(fā)時 , 單個 Library 模塊變成 Application 模塊 , 可以生成獨立運行的 APK 安裝包 ;
二、build.gradle 構建腳本分析
組件化實現(xiàn)需要依賴 Gradle ;
build.gradle 腳本都是使用 Groovy 語言編寫的代碼 , Groovy 也是 JVM 上語言 , 與 Java 語言完全兼容 , 其調(diào)用的 api 都是 Java 語言的 ; Android Studio 中的 Android 工程 , 在 Project 層級下有一個 build.gradle 構建腳本 , 在 Application 模塊 和 Library 模塊 中 , 也都各自存在一個 Module 級別的 build.gradle 構建腳本 ;
Project 下的 build.gradle 編譯時會被翻譯成 Project.java 類對象 , 該類路徑是 gradle-6.5\src\core-api\org\gradle\api\Project.java ;
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.4.31"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
其中的 buildscript , allprojects 等都是 Project.java 中的函數(shù) ;
@HasInternalProtocol
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
/**
* <p>Configures the build script classpath for this project.
*
* <p>The given closure is executed against this project's {@link ScriptHandler}. The {@link ScriptHandler} is
* passed to the closure as the closure's delegate.
*
* @param configureClosure the closure to use to configure the build script classpath.
*/
void buildscript(Closure configureClosure);
/**
* <p>Configures this project and each of its sub-projects.</p>
*
* <p>This method executes the given closure against this project and its sub-projects. The target {@link Project}
* is passed to the closure as the closure's delegate.</p>
*
* @param configureClosure The closure to execute.
*/
void allprojects(Closure configureClosure);
}
使用 Gradle 實現(xiàn)組件化 ( Gradle 變量定義與使用 )
一、頂層 Gradle 定義擴展變量
在 Project 層級的 build.gradle 中 , 使用 apply from: “component.gradle” , 引入 component.gradle 配置 ;
// 將 component.gradle 配置文件中的內(nèi)容導入到該位置
// 相當于引入頭文件
apply from: "component.gradle"
apply from 相當于引入頭文件 , 將 component.gradle 配置文件中的所有內(nèi)容 , 原封不動的拷貝到該語句所在位置 ;
component.gradle 是開發(fā)者自定義的一個配置文件 , 是使用 Groovy 語言編寫的 ;
ext 是 extension 擴展 , 通過 ext 可以定義擴展的變量 ;
component.gradle 文件內(nèi)容如下 : 全局使用的變量定義在這里 ;
// ext 是 extension 擴展的含義
// ext 后的 {} 花括號 , 是閉包 ,
ext{
// 定義 android 變量 , 類型是字典 Map 集合
// 其中定義了若干鍵值對集合
androidConfig = [
compileSdkVersion : 30,
minSdkVersion : 18,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
applicationId = [
"app" : "kim.hsl.component",
"mylibrary" : "",
"mylibrary2" : "",
]
}
在 Project 層級的 build.gradle 中定義的變量 , 可以在 Module 級的 build.gradle 中使用 ;
如 : 在上述 Project 層級的 build.gradle 中引入了 component.gradle 配置 , 那么在 Module 級別的 build.gradle 中可以通過 rootProject.ext.android 的方式調(diào)用在 component.gradle 定義的 android 變量 ;
在頂層構建腳本中定義的變量 , 在任何 Module 中的構建腳本都都可以獲取該變量 ;
二、獲取頂層的 Gradle 對象
在 build.gradle 中調(diào)用 rootProject , 相當于調(diào)用 Project 對象的 getRootProject() 方法 , 這是 Groovy 提供的語法糖 , 類似于 Kotlin 用法 ; 該方法返回一個 Project 對象 , 該對象代表根目錄下的 Project 層級的 build.gradle 構建腳本對應的 Project 對象 ;
rootProject 相當于頂層的 Project 級別的 build.gradle ;
Project 對應 getRootProject 方法源碼 :
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
String DEFAULT_BUILD_FILE = "build.gradle";
String PATH_SEPARATOR = ":";
String DEFAULT_BUILD_DIR_NAME = "build";
String GRADLE_PROPERTIES = "gradle.properties";
Project getRootProject();
}
三、Module 中使用 Gradle 變量
上面在頂層的 build.gradle 中定義了擴展變量 , 可以在 Module 下的 build.gradle 中獲取 ;
使用 rootProject.ext.androidConfig 代碼 , 可以獲取在頂層 component.gradle 中定義的 androidConfig 變量 ;
// 定義 android 變量 , 類型是字典 Map 集合
// 其中定義了若干鍵值對集合
androidConfig = [
compileSdkVersion : 30,
minSdkVersion : 18,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
將 rootProject.ext.androidConfig 變量賦值給本地的 def androidConfig 變量 , def 相當于 Java 中的 Object , def androidConfig 是聲明了一個新變量 , 名稱是 androidConfig ;
// def 相當于 Java 中的 Object
// 聲明 config 和 appId 變量 , 并為其賦值
def androidConfig = rootProject.ext.androidConfig
def appId = rootProject.ext.applicationId
通過調(diào)用 androidConfig.compileSdkVersion , 可以獲取 component.gradle 中定義的 androidConfig 變量中對應的 compileSdkVersion 鍵對應的值 30 ;
通過調(diào)用 androidConfig.minSdkVersion , 可以獲取 component.gradle 中定義的 androidConfig 變量對應的 minSdkVersion 鍵對應的值 18 ;
部分代碼示例 :
// def 相當于 Java 中的 Object
// 聲明 config 和 appId 變量 , 并為其賦值
def androidConfig = rootProject.ext.androidConfig
def appId = rootProject.ext.applicationId
android {
compileSdkVersion androidConfig.compileSdkVersion
buildToolsVersion "30.0.3"
defaultConfig {
applicationId appId["app"]
minSdkVersion androidConfig.minSdkVersion
targetSdkVersion androidConfig.targetSdkVersion
versionCode androidConfig.versionCode
versionName androidConfig.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
這樣做的好處是 , 可以統(tǒng)一管理 Android 應用的配置版本 , 不用每個 Module 都進行獨立修改 ;
四、Gradle 中打印變量值
在 build.gradle 中打印輸出變量值 , 在 Module 下的 build.gradle 中使用 println 函數(shù) 打印變量 , 變量放在 “${}” 中 , 打印結(jié)果在 編譯時輸出到 Build 面板中 ;
println("Print Variable : rootProject.ext.android : ${rootProject.ext.android}")
注意 : 不要打印中文 , 會出現(xiàn)亂碼 ;
五、涉及到的 Gradle 構建腳本
頂層 Gradle 代碼示例 :
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// 將 component.gradle 配置文件中的內(nèi)容導入到該位置
// 相當于引入頭文件
apply from: "component.gradle"
buildscript {
ext.kotlin_version = "1.4.10"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
引入的 Gradle 代碼 :
// ext 是 extension 擴展的含義
// ext 后的 {} 花括號 , 是閉包 ,
ext{
// 定義 android 變量 , 類型是字典 Map 集合
// 其中定義了若干鍵值對集合
androidConfig = [
compileSdkVersion : 30,
minSdkVersion : 18,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
applicationId = [
"app" : "kim.hsl.component",
"mylibrary" : "",
"mylibrary2" : "",
]
}
Module 層級的 Gradle 代碼示例 : Component\app\build.gradle 構建腳本 ;
plugins {
id 'com.android.application'
id 'kotlin-android'
}
println("Print Variable : rootProject.ext.androidConfig : ${rootProject.ext.androidConfig}")
// def 相當于 Java 中的 Object
// 聲明 config 和 appId 變量 , 并為其賦值
def androidConfig = rootProject.ext.androidConfig
def appId = rootProject.ext.applicationId
android {
compileSdkVersion androidConfig.compileSdkVersion
buildToolsVersion "30.0.3"
defaultConfig {
applicationId appId["app"]
minSdkVersion androidConfig.minSdkVersion
targetSdkVersion androidConfig.targetSdkVersion
versionCode androidConfig.versionCode
versionName androidConfig.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
使用Gradle實現(xiàn)組件化(組件模式與集成模式切換)
一、模塊化與組件化模式控制
在頂層 build.gradle 定義擴展變量 , 用于標識當前是 模塊化模式 還是 組件化模式 , 模塊化模式就是默認的模式 ;
// ext 是 extension 擴展的含義
// ext 后的 {} 花括號 , 是閉包 ,
ext{
// 是否是模塊化模式
// 集成模式 true ( 默認模式 , 模塊化 )
// 組件模式 false ( 組件化 )
isModuleMode = false
}
如果將變量定義在 build.gradle 構建腳本中 , 需要使用 ext 擴展變量 , 在 gradle.properties 可以直接定義 ;
在 Application Module 下的 build.gradle 保持不變 :
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
在 Library Module 下的 build.gradle 中控制當前 Module 是 Application 模塊還是 Library 模塊 ;
// 根據(jù) isModuleMode 動態(tài)切換 集成模式 / 組件模式
if (isModuleMode){
// 集成模式
apply plugin: 'com.android.library'
}else{
// 組件模式
apply plugin: 'com.android.application'
}
apply plugin: 'kotlin-android'
二、applicationId 設置
Application 模塊在 " android/defaultConfig " 層級下 , 必須有 applicationId ;
Library 模塊不需要配置 applicationId ;
這里需要進行分別處理 , 如果是 Library 模塊 , 在 組件模式 下 , 必須配置 " android / defaultConfig / applicationId " 選項 ; 但是在 集成模式 下 , 必須不能配置 " android / defaultConfig / applicationId " 選項 ;
主要配置如下 : 在 集成模式 下 , 不配置 applicationId ; 在 組件模式 下 , 才配置 applicationId 選項 ;
android {
defaultConfig {
if (!isModuleMode){
// 組件模式 : 必須配置 applicationId
applicationId appId["library1"]
}
}
}
注意 : 在 Application Module 下不進行上述配置 , 只有在 Library Module 下才進行上述配置 ;
三、使用 sourceSets 配置組件模式下使用的清單文件
使用 sourceSets 資源配置選項 , 可以配置 java , manifest , res , assets , 等目錄 ;
Android Studio 中
- 默認的 Java 代碼的路徑是 Component\app\src\main\java
- 默認的清單文件路徑是 Component\app\src\main\AndroidManifest.xml
- 默認的資源路徑是 Component\app\src\main\res
- 默認的 Assets 資源路徑是 Component\app\src\main\assets
在 Library Module 中 , 一般不會配置 Launcher Activity , 因此在 組件模式 下 , 需要指定一個新的 AndroidManifest.xml 清單文件 ;這里在 main 目錄下創(chuàng)建 component 目錄 , 組件化 相關文件 , 都放在該目錄中 ;下圖中 , 藍色矩形框中是默認的清單文件 , 在 集成模式 下的 Library Module 中使用 ; 紅色矩形框中是 組件模式 下使用的清單文件 , 在 Application Module 中使用 ;
配置 清單文件 示例 :
android {
compileSdkVersion androidConfig.compileSdkVersion
buildToolsVersion "30.0.3"
defaultConfig {
if (!isModuleMode){
// 組件模式 : 必須配置 applicationId
applicationId appId["library2"]
}
// 資源配置
sourceSets{
main{
if (!isModuleMode){
// 組件化模式下使用 ComponentAndroidManifest.xml 作為清單文件
manifest.srcFile 'src/main/component/AndroidManifest.xml'
}
}
}
}
}
四、組件模式 與 集成模式 切換示例
將 component.gradle 中的 isModuleMode 變量設置為 true , 當前的模式就是默認的模塊化模式 ;
編譯后效果如下 : 1個 Application 應用 , 2個 Library 應用 ;
將 component.gradle 中的 isModuleMode 變量設置為 false , 當前的模式就是 組件模式 ;
編譯后效果如下 : 3 個 Application 應用 ;
五、完整的 Gradle 配置
1、Project 層級的 build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// 將 component.gradle 配置文件中的內(nèi)容導入到該位置
// 相當于引入頭文件
apply from: "component.gradle"
buildscript {
ext.kotlin_version = "1.4.10"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
2、Project 層級的擴展變量定義
// ext 是 extension 擴展的含義
// ext 后的 {} 花括號 , 是閉包 ,
ext{
// 是否是模塊化模式
// 集成模式 true ( 默認模式 , 模塊化 )
// 組件模式 false ( 組件化 )
isModuleMode = false
// 定義 android 變量 , 類型是字典 Map 集合
// 其中定義了若干鍵值對集合
androidConfig = [
compileSdkVersion : 30,
minSdkVersion : 18,
targetSdkVersion : 30,
versionCode : 1,
versionName : "1.0"
]
applicationId = [
"app" : "kim.hsl.component",
"library1" : "kim.hsl.library1",
"mylibrary2" : "kim.hsl.library2",
]
// androidx 版本號
androidxVersion = "1.2.0"
// 統(tǒng)一管理依賴庫
dependencies = [
// ${} 表示引用之前定義的變量
"appcompat" : "androidx.appcompat:appcompat:${androidxVersion}"
]
}
3、主應用的 build.gradle文章來源:http://www.zghlxwxcb.cn/news/detail-815865.html
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
println("Print Variable : rootProject.ext.androidConfig : ${rootProject.ext.androidConfig}")
// def 相當于 Java 中的 Object
// 聲明 config 和 appId 變量 , 并為其賦值
def androidConfig = rootProject.ext.androidConfig
def appId = rootProject.ext.applicationId
android {
compileSdkVersion androidConfig.compileSdkVersion
buildToolsVersion "30.0.3"
defaultConfig {
applicationId appId["app"]
minSdkVersion androidConfig.minSdkVersion
targetSdkVersion androidConfig.targetSdkVersion
versionCode androidConfig.versionCode
versionName androidConfig.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
if (isModuleMode){
// 集成模式下才能引用這兩個 Library Module
implementation project(':library1')
implementation project(':library2')
}
}
4、Library 模塊的 build.gradle文章來源地址http://www.zghlxwxcb.cn/news/detail-815865.html
// 根據(jù) isModuleMode 動態(tài)切換 集成模式 / 組件模式
if (isModuleMode){
// 集成模式
apply plugin: 'com.android.library'
}else{
// 組件模式
apply plugin: 'com.android.application'
}
apply plugin: 'kotlin-android'
println("Print Variable : rootProject.ext.androidConfig : ${rootProject.ext.androidConfig}")
// def 相當于 Java 中的 Object
// 聲明 config 和 appId 變量 , 并為其賦值
def androidConfig = rootProject.ext.androidConfig
def appId = rootProject.ext.applicationId
android {
compileSdkVersion androidConfig.compileSdkVersion
buildToolsVersion "30.0.3"
defaultConfig {
if (!isModuleMode){
// 組件模式 : 必須配置 applicationId
applicationId appId["library1"]
}
minSdkVersion androidConfig.minSdkVersion
targetSdkVersion androidConfig.targetSdkVersion
versionCode androidConfig.versionCode
versionName androidConfig.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// 資源配置
sourceSets{
main{
if (!isModuleMode){
// 組件化模式下使用 ComponentAndroidManifest.xml 作為清單文件
manifest.srcFile 'src/main/component/AndroidManifest.xml'
}
}
}
}
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.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
到了這里,關于Android學習之路(22) 從模塊化到組件化的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!