最近需求說要做一個定製化編譯,然後就開始研究gradle。以前以爲很簡單就是個編譯工具,偶爾配置下就好了。最後被打臉了,發現對其一點都不瞭解---一無所知。研究了三天只研究了個皮毛,寫下來總結下以後方便查閱。
一、gradle簡介
1.Android Gradle基礎
android應用程序使用開源工具Gradle構建。Gradle一種藝術API,非常容易的支持定製,並且在java世界有着廣泛的應用。Android爲了實現編譯、打包等,開發者開發了Android插件爲Gradle添加了一系列的新特徵,特別是在構建Android app上的應用,包括:構建類型、多樣化、簽名配置、庫文件工程等等功能。
Gradle中,每一個待編譯的工程都叫一個Project。每一個Project在構建的時候都包含一系列的Task。
1.1 Android Gradle構建文件
在我們使用Android Studio工具開發Android應用的時候,當創建一個新的Android工程,默認的Gradle構建文件包括了
setting.gradle
,build.gradle
和app/build.gradle
。具體位置如圖所示
build.gradle:配置其他子Project的,添加任務。
settings.gradle:它裏邊用來告訴Gradle,這個multiprojects包含多少個子Project。
二、任務Task建立
2.1、添加和執行任務
1、添加
在根目錄或者app目錄下的build.gradle中添加
task startDelTask {
doLast {
//remove "ijkplayer-java/"
println "this is startDelTask"
}
}
2、運行
通過圖形界面運行
雙擊圖形界面中的startDelTask即可。我的Task是添加在根目錄下的所以在根目錄下中other目錄(也可隨便點一個,輸入查找,選中後直接輸入即可查找)
通過命令運行
gradlew startDelTask
2.2、在編譯前執行
1、每一個編譯都行
task startDelTask {
doLast {
//remove "ijkplayer-java/"
println "this is startDelTask"
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn startDelTask
}
2、要判斷名稱
project.tasks.whenTaskAdded { Task theTask ->
if (theTask.name == 'assembleDebug') {
theTask.dependsOn(tangTangTest)
theTask.mustRunAfter(tangTangTest) // diyTask在assembleRelease之前執行
}
}
2.3、在任務後執行
Task diyTask = project.task('diyTask') {
doLast {
Utils.println("diy task run")
}
}
project.tasks.whenTaskAdded { Task theTask ->
if (theTask.name == 'assembleRelease') {
theTask.dependsOn(diyTask) // 編譯完apk之後再執行自定義task
}
}
三、多渠道定製化
多渠道還可以定製很多東西詳細這裏就不介紹了。還有什麼佔位符修改androidmaniface.xml修改也沒有介紹,網上很多資料,感興趣的可以去網上學習下。
android{
... ...
defaultConfig {
... ...
flavorDimensions "default"
}
... ...
productFlavors {
OTT {
dimension "default"
//設置buildConfig參數
buildConfigField "String", "FLAVORS_TYPE", '"OTT"'
}
OTT_DVB {
dimension "default"
buildConfigField "String", "FLAVORS_TYPE", '"OTT_DVB"'
}
}
productFlavors.all {
flavor -> flavor.manifestPlaceholders = [CHANNEL_VALUE: name]
}
}
四、AndroidManifest文件的替換
4.1、通過參數直接配置
首先在/src/main下建兩個文件夾(不一定一樣命名)。debug和release。兩個文件夾中放入不同的AndroidManifest.xml
然後在當前不要打包不同AndroidManifest文件的Module的build.gradle中寫入以下代碼:
判斷條件通過傳參,或者全局定義。
sourceSets {
main {
if (條件判斷) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/release/AndroidManifest.xml'
}
}
}
//如下5.1傳參
sourceSets {
main {
println "------>${getTinkerIdValue()}"
if (getTinkerIdValue().equals("OTT")) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/release/AndroidManifest.xml'
}
}
}
4.2、拷貝到編譯目錄上去
編譯目錄:VPSClient\app\build\intermediates\merged_manifests\OTTRelease
android {
... ...
applicationVariants.all { variant ->
variant.outputs[0].processManifest.doLast {
//渠道與配置名
String variantName = variant.name.capitalize()
//要替換的manifest文件
def manifestFile = "${projectDir}${File.separator}build${File.separator}intermediates${File.separator}merged_manifests${File.separator}${variantName}${File.separator}AndroidManifest.xml"
//源文件
def srcManifact = "${projectDir}${File.separator}configs${File.separator}${variant.productFlavors[0].name}.xml"
println "src: $srcManifact"
//通過源文件內容替換
def updatedContent = new File(srcManifact).getText('UTF-8')
new File(manifestFile).write(updatedContent, 'UTF-8')
}
}
... ...
}
4.3、選擇
選擇第二種方式,因爲第一種方式每次都需要傳參或者改gradle源碼及其不方便。第二種方式實現了自動化。
五、參數打包和代碼中使用
5.1、在gradle中多渠道打包時配置BuildConfig
productFlavors {
OTT {
dimension "default"
buildConfigField "String", "FLAVORS_TYPE", '"OTT"'
}
OTT_DVB {
dimension "default"
buildConfigField "String", "FLAVORS_TYPE", '"OTT_DVB"'
}
}
5.2、設置默認參數時配置BuildConfig
1、app/gradle 配置
android {
defaultConfig {
....
buildConfigField "String", "FLAVORS_TYPE ", "\"${getTinkerIdValue()}\""
}
}
//如果沒有參數,就使用 1.0.0_base 作爲名字
def getTinkerIdValue() {
return hasProperty("FLAVORS_TYPE") ? FLAVORS_TYPE : "OTT1"
}
2、打包命令傳參
gradlew assembleRelease -PFLAVORS_TYPE=OTT
5.3、java文件中使用
// BuildConfig 一定要導入 當前工程包名的,沒有的話 先build一次
((TextView)findViewById(R.id.textView)).setText(BuildConfig.FLAVORS_TYPE);
六、查看打印信息
在任意gradle.build中添加如下代碼:
class EggListener implements TaskExecutionListener {
@Override
void beforeExecute(Task task) {
}
@Override
void afterExecute(Task task, TaskState state) {
}
}
gradle.addListener(new EggListener ())
七、執行命令
7.1 官方標準
task stopTomcat(type:Exec) {
workingDir '../tomcat/bin'
//on windows:
commandLine 'cmd', '/c', 'stop.bat'
//on linux
commandLine './stop.sh'
//store the output instead of printing to the console:
standardOutput = new ByteArrayOutputStream()
//extension method stopTomcat.output() can be used to obtain the output:
ext.output = {
return standardOutput.toString()
}
}
7.2實例
task.dependsOn(exec)
task exec(type:Exec) {
def outputPath = "${getProjectDir()}\\src\\main\\assets"
// workingDir '../tomcat/bin'
//on windows:
commandLine 'cmd', '/c', "dx --dex --output ${outputPath}\\vossdk.dex ${outputPath}\\outfile\\classes.jar"
// commandLine "dx --dex --output ${getProjectDir()}/src/main/assets/vossdk.dex ${getProjectDir()}/src/main/assets/outfile/classes.jar"
//store the output instead of printing to the console:
doLast {
delete("${getProjectDir()}/src/main/assets/outfile")
}
standardOutput = new ByteArrayOutputStream()
//extension method stopTomcat.output() can be used to obtain the output:
ext.output = {
return standardOutput.toString()
}
}
這個實例其實不合規,可以將命令放進腳本中,運行腳本可以兼容Linux和window兩個系統。
結束語
以上就是本次分享的gradle 中定製化編譯,以及相關設置的實例。最後慣例給大家推介一下我們的技術工作號,歡迎大家來交流技術問題,謝謝!