Gradle是Android項目開發環境的一部分,Android Studio每次配置編譯時都需要使用Gradle。
一.Gradle基礎:
Gradle本質上時一個自動構建工具,使用基於Groovy的特定領域語言(DSL)來聲明項目設置。使用Groovy最大哦的原因就是Groovy基本語法和Java一樣,最大程度適應Java開發。當利用Groovy編寫自定義插件時,語法並沒有太大差異,僅僅是配置機制需要調整。
Android Studio構建工程時,就是利用Gradle編寫的插件來加載工程配置和編譯文件。
- 工程根目錄的build.gradle時配置整個工程引用的Gradle文件,能夠配置獲取Gradle引用倉庫的地址。
-
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { //構建腳本引用 repositories { //插件倉庫配置 google() jcenter() //jcenter本質上是一個maven倉庫 } dependencies { //依賴插件 classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' // 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 //刪除主路徑buildDir文件 } dependencies { //第三方庫的依賴 compile 'com.alibaba:arouter-api:1.1.0' annotationProcessor 'com.alibaba:arouter-compiler:1.1.1' compile('com.facebook.fresco:fresco:0.10.0') { exclude group:'' } }
根目錄的build.gradle文件會影響工程中其他模塊的build.gradle文件的引用倉庫的路徑
- 當創建出一個新的module時,每個module都會有一個build.gradle文件。
-
apply plugin: 'com.android.application' //引用編譯構建Gradle插件 android { compileSdkVersion 28 //編譯的工具對應版本 buildToolsVersion "29.0.0" //編譯工具對應版本 defaultConfig { //默認配置 applicationId "com.example.demo1" minSdkVersion 15 //最低支持版本 targetSdkVersion 28 //支持的目標版本 versionCode 1 //版本號 versionName "1.0" //版本名 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" //測試腳本 } buildTypes { //構建類型 release { //release版本配置 minifyEnabled false //是否打開混淆 shrinkResources true //是否打開資源混淆 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' //混淆文件 } } productFlavors { //用戶版本 client { manifestPlacehoders = [ channel:"10086", //渠道號 verNum:"1", //版本號 app_name:"Gank" //app名 ] } //服務版本 server { manifestPlacehoders = [ channel:"10087", //渠道號 verNum:"1", //版本號 app_name:"Gank服務版" //app名 ] } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' classpath 'com.android.tools.build:gradle:3.1.1' classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' implementation 'com.android.support:localbroadcastmanager:28.0.0'// add plugin // implementation "io.reactivex.rxjava3:rxjava:3.x.y" }
build.gradle的第一行代碼重點引用了構建需要用到的Gradle插件工具庫。對比編寫Java代碼,可以認爲相當於"import"了一個Java工具庫。
每個build.gradle自身是一個Project對象,project.apply()會加載某個工具庫到project對象中;
apply plugin:"XXX"的方法會將project對象傳遞入工具庫,然後再通過插件中的Groovy文件來操作project對象的屬性,以完善配置初始化信息;
其中android{}和dependencies{}是函數方程式,使用閉包函數的編寫方式。其真實調用android{}和dependencies{}方法。方法中會設置project的屬性;
每個Project中包含很多Task的構建任務,每個Task中可以包含很多Action動作,每個Action相當於一個代碼塊,包含很多需要被執行的代碼。
- Gradle對Android工程配置的地方,是主目錄中的settings.gradle的文件;
- 當添加一個module時,Gradle會自動添加一個文件路徑到settings.gradle中的include函數中,聲明這些文件夾以一個模塊的形式存在,將此模塊引用到Gradle中進行編譯構建。如移除其中一項,對應的文件夾將不會被Gradle插件識別,Android Studio也不會認爲此文件夾爲一個module目錄;
- 每個Gradle文件都是一個project對象,Gradle管理這個project的生命週期
Gradle的生命週期settings.gradle(初始化)->build.gradle(配置)->gradle(構建):
1.初始化階段會讀取根目錄中的settings.gradle的include信息,決定哪些工程會加入構建過程,並且創建project實例;
2.配置階段會按引用樹去執行所有工程的build.gradle腳本,配置project對象,一個對象由多個任務組件。此階段也會去創建/配置Task及相關信息;
3.運行階段會根據Gradle命令傳遞過來的Task名稱,執行相關依賴。
二.版本參數優化:
每個module的build.gradle文件都擁有一些必要的屬性,同一個Android工程中,在不同模塊中要求這些屬性一致,如complieSDKVersion/buildToolVersion等。如引用不一致,屬性不會被合併引入工程中,這樣一方面會造成資源的重複/包量增大,另一方面會降低編譯效率。
那就必有一個統一的/基礎的Gradle配置,以下有三種方案可選:
第一種方式使用共同參數的方式進行配置:
- 創建一個common_config.gradle文件;
- 在common_config.gradle中編寫一些簡單的變量信息;
-
project.ext { complieSdkVersion = 28 buildToolVersion = "29.0.0" minSdkVersion = 15 targetSdkVersion = 28 applicationId "com.example.demo1" }
- 在module的首行build.gradle中添加common_config文件,並通過引用類似引用靜態變量的方式來引用屬性。
-
apply plugin: 'com.android.library' apply plugin: '${rootProject.rootDir}/common_config.gradle' //引用額外的配置 android { compileSdkVersion project.ext.complieSdkVersion buildToolsVersion project.ext.buildToolVersion defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
第二種方式時使用Android對象配置:
因爲apply from引用了common_build.gradle,所以可以引用build.gradle中的Android對象。android{}中提供了android這個變量,可以進一步簡化android{}的參數編寫來簡化代碼。在common_build.gradle的project.ext中添加一個閉包方法來指定project對象變量。
-
setDefaultConfig = { extension -> //閉包參數extension相當於android對象 extension.compileSdkVersion compileSdkVersion extension.buildToolsVersion buildToolsVersion extension.defaultConfig { minSdkVersion minSdkVersion targetSdkVersion targetSdkVersion testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } extension.dataBinging{ enabled = true } }
然後在build.gradle中就可以使用setDefaultConfig了,類似屬性設置一樣,實質上是函數調用。
-
android { project.ext.setDefaultConfig android //調用配置函數 .... }
第三種方式時使用project對象配置:
-
//設置app 設置 setAppDefaultConfig = { extension -> extension.apply plugin:'com.android.application' //引入application插件庫 extension.descriptaion "app" setAndroidConfig extension.android setDependencies extension.dependencies } //設置Lib配置 setLibDefaultConfig = { extension -> extension.apply plugin:'com.android.application' extension.descriptaion "lib" setAndroidConfig extension.android setDependencies extension.dependencies } //設置Android配置 setAndroidDefaultConfig = { extension -> extension.compileSdkVersion 28 extension.buildToolsVersion "29.0.0" extension.defaultConfig { minSdkVersion 15 targetSdkVersion 28 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { arguments = [moduleName:extension.project.getName()] } } } extension.dataBinging{ enabled = true } } //設置依賴 setDependencies = { extension -> extension.compile fileTree(dir: 'lib','include',['*.jar']) //每個module都需要引用路由apt插件庫才能生成相應的代碼,這裏無須重複編寫每個module extension.annotationProcessor 'com.alibaba:arouter-compiler:1.1.1' }
在build.gradle中只需要傳入project對象到閉包函數中即可:
-
//application module配置 apply plugin: '${rootProject.rootDir}/common_config.gradle' //引用額外的配置 project.ext.setAppDefaultConfig project //library module配置 apply plugin: '${rootProject.rootDir}/common_config.gradle' //引用額外的配置 project.ext.setLibDefaultConfig project
因爲組件化會用到application module和library module,所以需要分開編寫共性的方法,可以爲一些公用的庫添加引用。
三.調試優化:
業務模塊調試,將單一模塊做成app啓動,然後用於調試測試。這樣保證單獨模塊可以分離調試。
需要變更的地方:
- 業務模塊是Library module,獨立調試需要將模塊做成application module才能引入app構建流程。在common_config.gradle中,只需替換對象即可。
- 每個Application都需要配置ApplicationId
可以使用直接配置屬性:
applicationId project.ext.applicationId
也可以在common_config中添加函數配置:
-
setAppDefaultConfig = { extension -> ... extension.android.defaultConfig{ applicationId applicationId+"."+extension.getName() } ... }
這裏extension.getName(),在默認ApplicationId後添加module名字,用於區分不同module產生的單獨的app。
- 在src中建立debug文件夾,同main文件夾目錄類似,用於放置需要調試AndroidManifest.xml文件/Java文件/res資源文件。AndroidManifest文件需要設置默認啓動activity文件,不給啓動activity設置Default屬性,否則會導致安裝後activity被啓動的問題:
-
<activity android:name=".DebugActivity" android:theme="@style/AppTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>
- 在common_config中需要聲明單獨模塊調試變量,如:
-
project.ext{ ... isNewDebug = false ... }
在模塊build.gradle中變量isNewDebug作爲開關:
-
if(project.ext.isNewDebug){ project.setLibDefaultConfig project //設置lib配置 }else { project.setAppDefaultConfig project //設置app配置 }
在sourceSets資源配置,配置AndroidManifest的地址及res資源地址,編譯構建是Gradle會選取內的資源:
-
sourceSets { main{ if (project.ext.isNewDebug) { manifest.srcFile 'src/debug/AndroidManifest.xml' res.srcDirs = ['src/debug/res','src/main/res'] }else { manifest.srcFile 'src/main/AndroidManifest.xml' resources { //排除Java/debug文件夾下的所有文件 exclude 'src/debug/*' } } } }
- app module需要移除已經單獨調試的模塊的依賴:
-
dependencies { if(!project.ext.isNewDebug){ compile project(':news') } }
四.資源引用配置:
Gradle有多種引用的方式
- 使用sourceSets的方式指定文件的路徑。sourceSets還可以指定更多的資源設置屬性的路徑:
-
sourceSets { main{ manifest.srcFile 'src/debug/AndroidManifest.xml' //指定AndroidManifest res.srcDirs = ['src/main/res'] //指定Java文件路徑 resources.srcDirs = ['src'] //全部資源文件路徑 aidl.srcDirs = ["src"] //指定aidl文件路徑 renderscript.srcDirs = ['src'] //指定renderscript文件路徑 res.srcDirs = ['src'] //res資源文件路徑 assets.srcDirs = ['assets'] //指定資產文件路徑 } }
- 可以動態添加res資源,在buildtype和productFlavor中定義resValue:
-
resValue "string","app_name",....
- 可以指定特定尺寸的資源,也可以在buildType和productFlavor中定義:
-
resConfigs "hdpi","xhdpi","xxhdpi"
- Gradle加載優先級爲BuildType->productFlavor->main->dependencies。