写给 Android 开发者的 Gradle 系列(二)撰写 task

2.2 在 build.gradle 中撰写 task 调用 GreetingTask 类:

// Use the default greeting
task (name: hello , type: GreetingTask)

// Customize the greeting
task (name: greeting , type: GreetingTask) {
greeting = ‘greetings from GreetingTask’
}

2.3 调用该 task——

./gradlew hello

Task :app:hello
hello from GreetingTask

./gradlew greeting

Task :app:greeting
greetings from GreetingTask

所以看到这里应该不仅能够理解 Task 类的书写,并且应该能够大致明白 type 这个参数的含义了。

不知道会不会和笔者一样事儿逼的读者此时会疑惑 @TaskAction 修饰的方法和 doLast {} 以及 doFirst {} 闭包的执行顺序是怎样的?

task (name: hello, type: GreetingTask) {
doFirst {
def list = getActions()
for (int i = 0; i < list.size(); i++) {
println list.get(i).displayName
}
}

doLast {

}
}

首先声明 doFirst {}doLast {} 闭包;然后戳进 DefaultTask 源码并追踪到顶级父类 AbstractTask 中可以看到内部通过使用 actions 存储所有执行的 Action,并通过 getAction() 暴露;actions 是 List 类型,内部的元素类型是 ContextAwareTaskAction,该接口又实现了 Describable,Describable 仅声明了一个 getDisplayName() 方法,所以可以直接通过 displayName 获取该 Action 的名称。

理解以上三步即可完成上述 task 撰写,在命令行中试试——

./gradlew hello

Task :app:hello
Execute doFirst {} action
Execute greet
Execute doLast {} action

Gradle 内部将会自动为变量设置 setter、getter 方法,所以当一个 Gradle 有 getXxx() 方法时,可以直接使用 xxx 变量。如果不清楚这个细节,建议回顾上一篇文章的附录。

task 依赖关系

开发者常使用 dependsOn 来指定依赖关系(另外两种是指定 task 执行顺序,详见文档 Task Dependencies and Task Ordering),如下:

task a {
doLast {
println ‘a’
}
}

task b {
dependsOn(‘a’)
doFirst {
println ‘b’
}
}

不妨将以上代码写在 app build.gradle 文件下,当执行 task b 的时候,会输出如下信息:

./gradlew task app:b

> Task :app:a

a

> Task :app:b

b

可以看到,由于 task b 需要依赖 task a,所以 task b 执行的时候会先执行 task a。

有经验的开发者如果在命令行中试过 assembleDebug 等命令会发现,它们的执行将会依赖于许多其他 task。所以不妨在命令行中试试 ./gradlew assembleDebug 观察输出结果。

task 实战

install && launch apk

com.android.application 自带 installDebug task,开发者可以使用 installDebug 安装当前项目 apk:

./gradlew installDebug

> Task :app:installDebug

Installing APK ‘app-debug.apk’ on ‘xxxxx’ for app:debug Installed on 1 device.

但是似乎看起来有些不尽人意的地方,例如开发者希望安装的时候能够顺带能够启动该 app。那么该如何做呢?

首先从问题的可行性上来进行分析,开发者的直觉告诉我们是可以通过 gradle 实现的——命令行可以安装、启动 apk——adb install -r app-debug.apkadb shell am start -n 包名/首 Activity。所以关键点就是如何通过 gradle 调用命令行代码以及如何获取到 包名/首 Activity 信息。

  1. 开发者的直觉同样告诉我们 Gradle 开发文档)中有关于命令行调用的信息,只需要使用 exec {} 闭包就好了。

  2. 如何获取 包名/首 Activity 信息?可以通过 AndroidManifest.xml 来获取。部分经验丰富的开发者知道——打入 apk 中的 AndroidManifest.xml 文件并不是我们平常写的 AndroidManifest.xml,而是 apk 编译后位于 Project/app/build/intermediates/manifests/full/debug/ 包下的 AndroidManifest.xml(当然,如果是 Release 包的话,应该是 Project/app/build/intermediates/manifests/full/release/ 包下)。

  • 包名就是 android 闭包下的 defaultConfig 闭包下的 applicationId

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 目标 Activity 则是包含 action 为 android.intent.action.MAIN 的 Activity。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

理解了以上内容,便不难理解下面的内容:

task installAndRun(dependsOn: ‘assembleDebug’) {
doFirst {
exec {
workingDir “KaTeX parse error: Expected 'EOF', got '}' at position 83: …app-debug.apk' }̲ exec { def pat…{buildDir}/intermediates/manifests/full/debug/AndroidManifest.xml”
// xml 解析
def parser = new XmlParser(false, false).parse(new File(path))
// application 下的每一个 activity 结点
parser.application.activity.each { activity ->
// activity 下的每一个 intent-filter 结点
activity.‘intent-filter’.each { filter ->
// intent-filter 下的 action 结点中的 @android:name 包含 android.intent.action.MAIN
if (filter.action.@“android:name”.contains(“android.intent.action.MAIN”)) {
def targetActivity = activity.@“android:name”
commandLine ‘adb’, ‘shell’, ‘am’, ‘start’, ‘-n’,
a n d r o i d . d e f a u l t C o n f i g . a p p l i c a t i o n I d / {android.defaultConfig.applicationId}/ android.defaultConfig.applicationId/{targetActivity}”
}
}
}
}
}
}

  1. install apk 的前提必须是得有一个 apk,所以势必需要依赖 assembleDebug task。

实际上 installDebug task 也是依赖 assembleDebug task 的,不妨可以试试——

task showInstallDepends {
doFirst {
println project.tasks.findByName(“installDebug”).dependsOn
}
}

./gradlew showInstallDepends

> Configure project :app

[task ‘installDebug’ input files, assembleDebug]

  1. exec 闭包中的几个参数提及下——

2.1 workingDir:工作环境,参数为 File 格式)。默认为当前 project 目录。

2.2 commandLine:需要命令行执行的命令,参数为 List 格式。

  1. 前一篇文章中提到 ——

说白了它们其实就是一些闭包、一些固定格式,正是因为它们的格式是固定的,task 才能够读取到相应的数据完成相应的事情。

在第二个 exec 闭包的第八行就很好的体现了这一点,通过 {android.defaultConfig.applicationId} 直接获取到 Gradle 文件中 android 闭包下的 defaultConfig 闭包下的 applicationId 的值。由此就获得了当前应用的包名。

当然,除了 Gradle 能够调用命令行以外,实际上 groovy 也是可以调用命令行的,但在此就不做扩展了。

  1. 至于最先启动的 Activity,肯定是 action 为 android.intent.action.MAIN 的 Activity,那么问题就是变成如何在 AndroidManifest.xml 中寻找到该 Activity 的事了——作为一个合格的老司机,应该能够想到 groovy 一定会提供相应的 xml 解析 API 的,至于具体的使用笔者就不在此扩展了,留给各位读者去源码中探索成长。

  2. 除去上面的信息以外,还需要什么?还需要知道一些 gradle 构建的信息——例如 debug 包会最终出现在 ${buildDir}/outputs/apk/debug;例如 debug 包中的 AndroidManifest.xml 并不是日常开发中写的那个 AndroidManifest.xml(虽然可能它俩基本没什么差异),而是 ${buildDir}/intermediates/manifests/full/debug 下的 AndroidManifest.xml。所以一是希望各位读者日常多去翻翻 build 文件夹,二是要知道 ${buildDir}(build 文件夹)有多么重要,因为 Gradle 构建 apk 的过程中,但凡有输出文件那么基本都会存在这个文件夹中,所以多去翻一翻。

由此之后,可以在命令行输入以下命令:

./gradlew installAndRun

> Task :app:installAndRun

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

愿你有一天,真爱自己,善待自己。

本文在开源项目:Android开发不会这些?如何面试拿高薪 中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

加下面V无偿领取!(备注Android)**
[外链图片转存中…(img-ijs63lWL-1710687606605)]

最后

愿你有一天,真爱自己,善待自己。

本文在开源项目:Android开发不会这些?如何面试拿高薪 中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值