关于gradle你应该知道的一些小事

更新时间:2023-05-26 11:18:16 阅读: 评论:0

关于gradle你应该知道的⼀些⼩事
前⾔
gradle的定义(来⾃维基百科)
Gradle是⼀个基于Apache Ant和Apache Maven概念的项⽬⾃动化建构⼯具。它使⽤⼀种基于Groovy的特定领域语⾔来声明项⽬设置,⽽不是传统的XML。当前其⽀持的语⾔限于Java、Groovy和Scala,计划未来将⽀持更多的语⾔。
通俗的理解:gradle是⼀种构建⼯具,我们可以⽤他来对多⼯程进⾏各种管理(依赖,打包,部署,发布,各种渠道的差异管理);
有些时候,我们会有⼀些个性化的构建需求,⽐如我们引⼊了第三⽅库,或者我们想要在通⽤构建过程中做⼀些其他的事情,这时我们就要⾃⼰在系统默认构建规则上做⼀些修改。这时候我们就要⾃⼰向Gradle”下命令“了,这时候我们就需要⽤Gradle能听懂的话了,也就是Groovy。
我们在开头处提到“Gradle是⼀种构建⼯具”。实际上,当我们想要更灵活的构建过程时,Gradle就成为了⼀个编程框架——我们可以通过编程让构建过程按我们的意愿进⾏。也就是说,当我们把Gradle作为构建⼯具使⽤时,我们只需要掌握它的配置脚本的基本写法就OK了;⽽当我们需要对构建流程进⾏⾼度
定制时,就务必要掌握Groovy等相关知识了。
遭遇的问题
我们在实时多项⽬构建的时候经常遇到以下这些问题:
1、同时依赖了不同版本的某个库,编译时出现duplicate class错误;
2、gradle 不同版本api报错;
3、不会写gradle配置,看不懂gradle语法,不知道从何学起;
4、对编译过程中gradle的报错⽆从下⼿;
等等…
我们接下来将从实际项⽬出发⼀步⼀步来学习gradle的这些事,本⽂主旨在于学习gradle的思路,深度细节将会忽略;
揭开Gradle的⾯纱
⼀、理解打包命令 gradle clean asmbleDebug/asmbleRelea
以上这条命令可以分解为三个部分,gradle,clean, asmbleDebug;实际上就和我们执⾏脚本⼀样,gradle是执⾏器,⽽clean 和 asmbleDebug是⼊参,在这⾥它们两个代表不同的task,就类似gradle task1 task2 这样。
⼆、什么是task?
在adle写上
task task1 {
println "===>task 1"
}
task task2 {继续奋斗
println "===>task 2"
}
这样就定义了两个task;当我们执⾏gradle task1 task2 -q的时候(-q是设置⽇志级别),理论上会看
到⽇志输出:
===>task 1
===>task 2
task的关系有dependsOn,mustRunAfter等等,由于项⽬中⽤的⽐较少这⾥先跳过这部分;
这⾥我们简单讲⼀下闭包的概念:
闭包在groovy中是⼀个处于代码上下⽂中的开放的,匿名代码块。它可以访问到其外部的变量或⽅法,
更详细的请⾃⾏google
然⽽,当我们在项⽬⾥执⾏gradle task1 task2 -q的时候,我们发现输出是这样的:
SeeyouClient git:(SeeyouClient-dev) ✗ gradle task1 task2 -q
doPackage value:Fal
Configuration 'compile' in project ':app' is deprecated. U 'implementation' instead.
==============anna apply start==================
configuration do:
include
蚂蚁照明
**  onClick
**  onItemClick
**  onCheckedChanged
**  onItemSelected
**  onSwitchButtonCheck
**  onItemLongClick
**  onLongClick
**  onPullRefresh
**  OnRefresh
configuration do:
exclude
org/conscrypt/
configuration do:
exceptions
java/lang/Exception
java/lang/NullPointerException
configuration do:
switch
custom
need inject=fal
==============anna apply end==================
Configuration 'provided' in project ':app' is deprecated. U 'compileOnly' instead.
Configuration 'debugCompile' in project ':app' is deprecated. U 'debugImplementation' instead.
===>task 1
===>task 2
DexKnife: Processing Variant
DexKnife: processSplitDex true
DexKnife: processing Task
----------------------tinker build warning ------------------------------------
人力资源案例tinker auto operation:
excluding annotation processor and source template from app packaging. Enable dx jumboMode to reduce package size.
enable dx jumboMode to reduce package size.
disable preDexLibraries to prevent ClassDefNotFoundException when your app is booting.老师你好
disable archive dex mode so far for keeping dex apply.
tinker will change your build configs:
we will add TINKER_ID=117 in your build output manifest file build/intermediates/manifests/full/*
if minifyEnabled is true
you will find the gen proguard rule file at build/intermediates/tinker_intermediates/tinker_proguard.pro
and we will help you to put it in the proguardFiles.
if multiDexEnabled is true
you will find the gen multiDexKeepProguard file at build/intermediates/tinker_intermediates/tinker_multidexkeep.pro
and we will help you to put it in the MultiDexKeepProguardFile.
if applyResourceMapping file is exist
we will build app apk with file
if resources.arsc has changed, you should u applyResource mode to build the new apk!
-----------------------------------------------------------------
Task spend time:
这是为什么呢?原因是gradle具有⾃⼰的⽣命周期:
初始化阶段:负责判断有多少个Projects参与构建:
先执⾏adle
配置阶段:负责对初始化阶段创建的Projects完成配置:
⽐如添加Task,修改Task的⾏为,闭包的内容会被执⾏,执⾏adle的内容;
执⾏阶段:根据配置阶段的配置执⾏任务:
执⾏task对应的内容,如doLast,doFirst之类的
因此gradle task1 task2 -q的输出⽇志就可以理解了,其实按照task1和task2的写法,执⾏gradle task1 task2 -q和gradle -q实际上效果是⼀样的。
三、gradle clean assmebleDebug到底做了什么?(源码追踪和依赖分析出编译流程)
1、打开gradle-4.5.1/bin/gradle⽂件可以看到执⾏了代码:
eval t -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-adle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" adle.launcher.GradleMain "$APP_ARGS"最终调⽤exec "$JAVACMD" "$@"来执⾏;所以⼊⼝就是:adle.launcher.GradleMain
2,最终会调⽤DefaultGradleLauncher⾥,我们可以很明确的看到它的⽣命周期:
这边最需要注意的时候,当我们只执⾏gradle -q这样的时候,实际上每⼀次都会执⾏到TaskGraph的阶段;也就是所有的tasks都已经梳理完成;
public class DefaultGradleLauncher implements GradleLauncher {
//(这⾥是4.5.1的版本,⽣命周期更细致化)
private enum Stage {
Load, LoadBuild, Configure, TaskGraph, Build, Finished
}
//2.14.1的版本则是:
private enum Stage {
Load, Configure, Build
}
//核⼼⽅法
private void doBuildStages(Stage upTo) {
try {
loadSettings();
if (upTo == Stage.Load) {
return;
}
configureBuild();
if (upTo == Stage.Configure) {
return;
}
constructTaskGraph();
酸枣的功效与作用
if (upTo == Stage.TaskGraph) {
return;
}
runTasks();
finishBuild();
} catch (Throwable t) {
Throwable failure = ansform(t);
finishBuild(new BuildResult(upTo.name(), gradle, failure));
throw new ReportedException(failure);
}
}
/
/调⽤时机
@Override
public SettingsInternal getLoadedSettings() {
doBuildStages(Stage.Load);
return ttings;
}
@Override
public GradleInternal getConfiguredBuild() {
doBuildStages(Stage.Configure);
return gradle;
}
public GradleInternal executeTasks() {
doBuildStages(Stage.Build);
return gradle;
}
四、知道编译流程后有什么⽤呢?
1、我们经常在adle看到这样的代码:
project.afterEvaluate {...}
android.applicationVariants.all {...}
gradle.addListener(new TaskListener())
apply from '../adle'
...
这⾥我们介绍⼏个概念:
project
对应gradle源码的Project.java(按住control点击project会⾃动跳转),⾥边提供⼀些对外的⽅法,如afterEvalute,beforeEvalue;在理解编译流程后,才能灵活的使⽤这些api;
android
对应gradle插件的AppExtension.java⽂件,提供了⼀些对外的参数和⽅法,我们可以使⽤来访问adle⾥的任意参数和⽅法;
gradle
对应的gradle源码⾥的Gradle.java对象,也是提供了⼀系列的⽅法给外部使⽤;
那么接下来假设我们有这样⼀个需求:找到⼀个叫cleanBuildCache的task,找到之后添加⼀个action,打印⼀⾏字;要实现这个需求,⾸先我们如何遍历这个app的所有task:
有很多种写法:
project.tasks.each {
task->
println "taskName:"+Name()
}
}
project.afterEvaluate {
project.tasks.each {
task->
println "taskName:"+Name()
}
XY散点图
}
执⾏gradle -q感受⼀下。
接下看看如何添加action
project.afterEvaluate {
project.tasks.each {
task->
// println "taskName:"+Name()
Name().equals("cleanBuildCache")){
println "find cleanBuildCache"
List<Action<? super Task>> list = new ArrayList<>()
list.add(new Action<Task>() {
@Override
void execute(Task task1) {
println 'excute cleanBuildCache action '
}
})
task.tActions(list)
}
}
}
执⾏gradle cleanBuildCache感受⼀下,
你会看到‘excute cleanBuildCache action '的打印字样;
那为什么⼀定要放在afterEvaluate之后呢,因为这样tasksGrap完成才有那么多task让你遍历,这就是理解⽣命周期所带来的好处。
2、现在回顾我们之前主app写的代码:
processProductDebugManifest;
project.tasks.each {
task->
Name().equals("processZroTestDebugManifest")){
println 'find processZroTestDebugManifest task:'
task.outputs.files.each {
file ->
println 'AbsolutePath():'+ AbsolutePath()
/
/AbsolutePath():/Urs/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/intermediates/manifests/instant-run/zroTest/debug
//AbsolutePath():/Urs/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/intermediates/manifests/full/zroTest/debug
//AbsolutePath():/Urs/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/outputs/
}
task.doLast {
println 'excute processZroTestDebugManifest task'
def dated = new Date().format("MMdd HH:mm")
def manifestFile = "${buildDir}/intermediates/manifests/full/zroTest/l"
def updatedContent = new File(manifestFile).getText('UTF-8')
.replaceAll("MY_APP_PKGNAME", "${MY_APP_PKGNAME}")
.replaceAll("MY_JPUSH_APPKEY", "${JPUSH_APPKEY}")
.replaceAll("MY_HUAWEI_APPKEY", "${HUAWEI_APPKEY}")
.replaceAll("MY_MEIZU_APPKEY", "${MEIZU_APPKEY}")
.replaceAll("MY_MEIZU_APPID", "${MEIZU_APPID}")
// .replaceAll("MY_XIAOMI_APPKEY", "${XIAOMI_APPKEY}")
// .replaceAll("MY_XIAOMI_APPID", "${XIAOMI_APPID}")
//.replaceAll("cn.jpush.android.rvice.PluginXiaomiPlatformsReceiver","ssage.mipush.XiaomiReceiver")
.replaceAll("MY_APK_VERSION", "${archivesBaName}-${dated}")
new File(manifestFile).write(updatedContent, 'UTF-8')
}
}
}
实际上也可以写成这样(但是这样因为变种不确定,写死成zroTest/debug,所以还是⽤上⾯的⽅法⽐较好,直接替换所有的变种):
project.tasks.each {
task->
Name().equals("processZroTestDebugManifest")){
println 'find processZroTestDebugManifest task:'+task.outputs.files.each {
file ->
}
task.doLast {
println 'excute processZroTestDebugManifest task'
def dated = new Date().format("MMdd HH:mm")
def manifestFile = "${buildDir}/intermediates/manifests/full/zroTest/l"
def updatedContent = new File(manifestFile).getText('UTF-8')
.replaceAll("MY_APP_PKGNAME", "${MY_APP_PKGNAME}")
.replaceAll("MY_JPUSH_APPKEY", "${JPUSH_APPKEY}")
.replaceAll("MY_HUAWEI_APPKEY", "${HUAWEI_APPKEY}")
.replaceAll("MY_MEIZU_APPKEY", "${MEIZU_APPKEY}")
.replaceAll("MY_MEIZU_APPID", "${MEIZU_APPID}")
// .replaceAll("MY_XIAOMI_APPKEY", "${XIAOMI_APPKEY}")
// .replaceAll("MY_XIAOMI_APPID", "${XIAOMI_APPID}")
//.replaceAll("cn.jpush.android.rvice.PluginXiaomiPlatformsReceiver","ssage.mipush.XiaomiReceiver")
.
replaceAll("MY_APK_VERSION", "${archivesBaName}-${dated}")
new File(manifestFile).write(updatedContent, 'UTF-8')
}
}
}
3、如何知道某个task⼲了什么呢,⽐如processZroTestDebugManifest或者Clean:
这些是ls.build到源码⾥寻找或者直接compile ‘ls.build:gradle:3.0.1'直接从依赖库⾥看源码;或者直接下载源码(⼤概30G左右):$ mkdir gradle_2.3.0
$ cd gradle_2.3.0
$ repo init -u /platform/manifest -b gradle_2.3.0
$ repo sync
⼤部分tasks都在com.adle.tasks⽂件夹下,⽐如:ManifestProcessorTask和CleanBuildCache
4、如何查找某个task的依赖呢,⽐如我想知道assmebleZroTestDebug执⾏后最终执⾏了哪些task;
1、编译后打印;
gradle.addListener(new TaskListener())
class TaskListener implements BuildListener,TaskExecutionListener {
private List<String> tasks = new ArrayList<>();
@Override
void buildStarted(Gradle gradle) {
}
@Override
void ttingsEvaluated(Settings ttings) {
}
@Override
void projectsLoaded(Gradle gradle) {
}
@Override
void projectsEvaluated(Gradle gradle) {
}
@Override
void buildFinished(BuildResult result) {
StringBuilder stringBuilder = new StringBuilder();
for(String taskName:tasks){
stringBuilder.append(taskName).append("\n")
}
println("任务列表:\n"+String())
}
@Override
void beforeExecute(Task task) {
}
@Override
void afterExecute(Task task, TaskState state) {
//println("===>Task:"+Name())
tasks.Name())格林童话故事全集
}
}
2、不⽤编译直接打印;
void printTaskDependency(Task task, String divider) {
divider += "-------"
println(divider+ it.getPath())
if (it.getPath().contains(":app")) {
printTaskDependency(it,divider)
}
}
}
project.tasks.all {
//println(" it:"+it.getName()+"==&Path:"+it.getPath())
if (it.getPath().equals(":app:asmbleZroTestDebug")) {
//Path())
printTaskDependency(it,"")
}
}
}
5、常⽤技能写人外貌的作文
1、gradle :app:dependencies > 1.txt 分析整个app的aar依赖
可以⽤于排查依赖库异常的问题;
请注意!:对⼯程依赖⽆效;
2、productFlavors和buildType概念,组合成变种如:
productFlavors {
branchOne {
applicationId "ample.branchOne"
buildConfigField "String", "CONFIG_ENDPOINT", "/android"
}
branchTwo {
applicationId "ample.branchTwo"
buildConfigField "String", "CONFIG_ENDPOINT", ""
}
}
dependencies {
compile 'com.android.support:support-v4:22.2.0'
branchOneCompile 'com.android.support:appcompat-v7:22.2.0'//只为branchOne添加这个依赖
}
3、排除依赖和强制使⽤某个版本和强制排除某个库:
configurations.all {
resolutionStrategy {
//  force 'org.javassist:javassist:3.18.2-GA'
/
/ don't cache changing modules at all
cacheChangingModulesFor 0, 'conds'
//  //强制模块使⽤指定版本号(防⽌其他模块使⽤、跟主⼯程不匹配的版本:
forcedModules = [
"iyou:peroid.ba:${PERIOD_BASE_VERSION}",
'org.javassist:javassist:3.18.2-GA'//"org.javassist:javassist:3.20.0-GA"//
, 'le.guava:guava:18.0'//'le.guava:guava:19.0-rc2'//
]
exclude group: 'com.squareup.okhttp3'
exclude group: 'de.findbugs', module: 'annotations'
}
}
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,如果有疑问⼤家可以留⾔交流,谢谢⼤家对的⽀持。

本文发布于:2023-05-26 11:18:16,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/932646.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:构建   依赖   配置   编译   源码   学习   流程
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图