2024年安卓最全写给 Android 开发者的 Gradle 系列(二)撰写 task,2024年最新字节跳动前端面试问题及答案

文末

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

根据官方文档Task#create() 可以知道,task 的基本写法可以是如下四种:

task myTask
task myTask { configure closure }
task (myTask) { configure closure }
task (name: myTask) { configure closure }

每一个 task 都有自己的名字,这样开发者才能调用它,例如调用上面的 task:

./gradlew myTask

但是有一个问题,倘若当前项目的 app module 和 a module 都含有一个名为 myTask 的 task,那么会不会起冲突,该如何调用它们?答案是不会冲突,调用方式如下:

./gradlew app:myTask(调用 app module 的 myTask)

./gradlew a:myTask(调用 a module 的 myTask)

通过 ProjectName:taskName 的形式便可以指定唯一绝对路径去调用指定 Project 的指定 task 了。

扩展

根据 Task#create() 可以知道,task 的创建是可以声明参数的,除了上述的 name 参数之外,还有如下几种:

  • type:默认为 DefaultTask。类似于父类。在后文中将会提及该参数。

  • dependsOn:默认为[]。希望依赖的 tasks,等同于 Task.dependsOn(Object... path) 中的 path。在后文中将会提及该参数。

  • action:默认为 null。等同于 Task.doFirst { Action } 中的 Action。

task (name: actionTest, action: new Action() {
@Override
void execute(Task task) {
println ‘hello’
}
}) {
}

等同于

task (name: actionTest) {
doFirst {
println ‘hello’
}
}

  • override:默认为 false。是否替换已存在的 task。

  • group:默认为 null。task 的分组类型。

  • description:默认为 null。task 描述。

  • constructorArgs:默认为 null。传给 task 构造函数的参数。

后面四种大部分开发过程中应该不怎么会用到,有需要的读者自行查阅文档。

task 内容格式

  1. 根据官方文档以及前一篇文章中可以知道,如果想给 task 添加操作,可以添加在 doLast {}/doFirst {} 等闭包中,例如:

task myTask {
doFirst {
println ‘myTask 最先执行的内容’
}
doLast {
println ‘myTask 最后执行的内容’
}
// warning
// println ‘Configuration 阶段和 Execution 阶段皆会执行’
}

切记大部分的内容是写在 doLast{}doFirst{} 闭包中,因为写在如果写在 task 闭包中的话,会在 Configuration 阶段也被执行。

  1. 根据官方文档可知,为了提高 task 复用性,Gradle 还支持 Task 类的书写——

2.1 将下述代码写在 build.gradle 中,并用 @TaskAction 标记想要执行的方法。

class GreetingTask extends DefaultTask {
String greeting = ‘hello from GreetingTask’

@TaskAction
def greet() {
println greeting
}
}

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’,

尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

题汇总。)

[外链图片转存中…(img-4LlVuajS-1715795375590)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值