最近需求说要做一个定制化编译,然后就开始研究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两个系统。