Kotlin(3)-协程和操作符重载

  • 继承

  • 修饰符

  • 空指针问题

正文

重难点

协程

想了很久,关于协程的内容,在官网上确实有很多内容,基础知识概念,基本使用,以及 流操作,通道,异常处理,并发处理等,不方便在这里展开。具体的参照:Kotlin中文网

这里具体去学习。本章节,只总结一下近期查阅资料并经本人验证的知识点。

概念

英文 coroutines : /,kəuru:'ti:n/ 意: 协同程序。 简称协程。

Kotlin提出协程概念,是为了简化异步编程,让开发者更容易控制函数的执行流程。

协程和线程的联系和区别

在操作系统OS中,进程资源分配的最小单位线程任务调度的最小单位。而协程则是处在线程内部的**“微线程”,或者说轻量级线程**。 由于线程在OS中是稀缺资源,所有OS平台的线程数量都是有上限的,平时编程我们会用线程池来管理线程数量,熟悉线程池的同学应该知道,线程池管理线程,无论是核心线程还是非核心线程,都不会随意去创建,除非迫不得已。

线程解决异步问题

  • 多线程同步编程可以通过加锁解决数据的线程安全问题,但是加锁会降低程序执行效率,并且锁多了,会有死锁隐患
  • 线程的状态转换完全由内核控制,程序员开发者无法干涉
  • 线程的是稀缺资源,不能随意创建,使用线程解决异步问题,线程的初始化,上下文切换(CPU轮转),线程状态切换(sleep,yield…), 变量加锁操作(synchronized关键字),都会使得线程的使用代价比较大

协程解决异步问题

  • 协程是运行在线程之上的优化产物,或称“微线程”。协程依赖线程运行,复杂的底层逻辑被封装在库内,使用时无需关心所处线程状态
  • 使用协程,开发者可以自己控制协程的状态(suspend挂起,resume恢复),而不会像线程那样依赖底层调度,时间片争夺。
  • 一个线程可以跑多个协程,一个协程也可以分段在多个线程上执行
  • 协程 是 非阻塞的,当前协程挂起之后,所在线程资源并不会浪费,它会去执行其他协程(如果有的话)
  • 协程 相对于线程这种OS中的稀缺资源,它是极其轻量级的,就算你开一百万个协程,对于系统的压力也不会像大量线程那样大(别说一百万个,linux系统的线程数量上线是1000,超过这个值系统就无法正常运行).
  • 总之一句话 : 协程的出现,让程序开发者对程序逻辑的掌控提升到了一个新的境界,想象一下,一个函数正在执行,你想让他在某个时刻暂停,然后在另一个时刻继续。利用线程恐怕很难做到。协程中,轻而易举。
基本使用

module级别的build.gradle 中 引入库依赖,推荐使用最新版(目前稳定版是1.3.3)

dependencies {
//…
implementation “org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3”
implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3” // 如果你需要用到协程调度器Dispatchers的话,必须加上这个依赖
}

协程的创建

创建协程有多种方式,全局/独立,同步/异步,并且可以指定 “协程调度器”

  • runBlocking

fun main() {
runBlocking {
println(“这是runBlocking协程…”)
}
}

  • launch

fun main() {
runBlocking {
println(“这是runBlocking协程…”)
launch {
println(“这是runBlocking内部的runBlocking协程…”)
delay(2000)
println(“延迟了2000MS之后,再打印”)
}
}
}

  • GlobalScope.launch

fun main() {
println(“协程,相对于主线程来说,都是异步的,也就是说,你在这里插入协程逻辑,主线程的逻辑并不会被阻塞”)
GlobalScope.launch {
delay(3000)
println("GlobalScope.launch 创建协程 ")
}
println(“主线程继续”)
Thread.sleep(5000)
}

  • Global.async

fun main() {
runBlocking {
val async: Deferred = GlobalScope.async {
println(“这是一个异步协程,他将返回一个Deferred”)
delay(2000)
“异步任务返回值”
}
println(“主线程继续:” + async.await())
}

Thread.sleep(5000)
}

“骚操作”

关心协程的人一般都会十分关注它到底能给我们异步编程带来怎样的便利。这里总结几个不用协程实现起来很麻烦的骚操作

  • 如果有一个函数,它的返回值需要等到多个耗时的异步任务都执行完毕返回之后,组合所有任务的返回值作为 最终返回值

fun test6(): String = runBlocking {
var finalRes = “”
coroutineScope {
launch {
delay(1000)
finalRes = finalRes.plus(“1”)
}
launch {
delay(2000)
finalRes = finalRes.plus(“2”)
}

launch {
delay(3000)
finalRes = finalRes.plus(“3”)
}
}
finalRes
}

fun main() {
val test6 = test6()
println(“最终返回值是: $test6”)
}

最终返回结果为(延迟3秒之后打印):

最终返回值是: 123

  • 如果有一个函数,需要顺序执行多个网络请求,并且后一个请求依赖前一个请求的执行结果

import kotlinx.coroutines.*

suspend fun getToken(): String {
for (i in 0…10) {
println(“异步请求正在执行:getToken :$i”)
delay(100)
}
return “ask”
}

suspend fun getResponse(token: String): String {
for (i in 0…10) {
println(“异步请求正在执行:getResponse :$token $i”)
delay(100)
}

return “response”
}

fun setText(response: String) {
println(“setText 执行,时间: ${System.currentTimeMillis()}”)
}

fun main() {
GlobalScope.launch(Dispatchers.Unconfined) {
var token = GlobalScope.async(Dispatchers.Unconfined) {
return@async getToken()
}.await() // 创建异步任务,并且 阻塞执行 await 是阻塞执行取得结果

var response = GlobalScope.async(Dispatchers.Unconfined) {
return@async getResponse(token)
}.await() // 创建异步任务,并且立即执行

setText(response)
}

Thread.sleep(20000)
}

执行结果:

异步请求正在执行:getToken :0
异步请求正在执行:getToken :1
异步请求正在执行:getToken :2
异步请求正在执行:getToken :3
异步请求正在执行:getToken :4
异步请求正在执行:getToken :5
异步请求正在执行:getToken :6
异步请求正在执行:getToken :7
异步请求正在执行:getToken :8
异步请求正在执行:getToken :9
异步请求正在执行:getToken :10
异步请求正在执行:getResponse :ask 0
异步请求正在执行:getResponse :ask 1
异步请求正在执行:getResponse :ask 2
异步请求正在执行:getResponse :ask 3
异步请求正在执行:getResponse :ask 4
异步请求正在执行:getResponse :ask 5
异步请求正在执行:getResponse :ask 6
异步请求正在执行:getResponse :ask 7
异步请求正在执行:getResponse :ask 8
异步请求正在执行:getResponse :ask 9
异步请求正在执行:getResponse :ask 10
setText 执行,时间: 1578904290520

  • 当前正在执行一项异步任务,但是你突然不想要它执行了,随时可以取消

fun main() {
// 协程任务
val job = GlobalScope.launch(Dispatchers.IO) {
for (i in 0…100){// 每次挂起100MS,100次也就是10秒
println(“协程正在执行 $i”)
delay(100)
}
}

// 但是我在1秒之后就取消协程
Thread.sleep(1000)
job?.cancel()
println( “btn_right 结束协程”)
}

执行结果(本该执行100轮的打印,只持续了10轮):

协程正在执行 0
协程正在执行 1
协程正在执行 2
协程正在执行 3
协程正在执行 4
协程正在执行 5
协程正在执行 6
协程正在执行 7
协程正在执行 8
协程正在执行 9
btn_right 结束协程

Process finished with exit code 0

  • 如果你想让一个任务最多执行3秒,超过3秒则自动取消

import kotlinx.coroutines.*

fun main() = runBlocking {
println(“限时任务中结果是:” + getResFromTimeoutTask())
}

suspend fun getResFromTimeoutTask(): String? {
// 忘了,它会保证内部的协程代码都执行完毕,所以不能这么写
return withTimeoutOrNull(1300) {
for (i in 0…10) {
println(“I’m sleeping $i …”)
delay(500)
}
“执行结束”
}
}

执行结果

I’m sleeping 0 …
I’m sleeping 1 …
I’m sleeping 2 …
限时任务中结果是:null

Process finished with exit code 0

总结

协程作为kotlin 区别于java的新概念,它的出现是为了解决java不好解决的问题,比如层层回调导致代码臃肿,比如 异步任务执行流程不好操控等。本章节篇幅有限,无法展开说明,但是对于新手而言,看完本章应该能对协程的作用有一个大概的认知。本人也是初步研究,后续有更深入的了解之后,再进行专文讲解吧。


操作符重载

概念

说人话,像是一元操作符 ++自加,二元操作符 +相加 ,默认只支持数字类型,比如Int. 但是通过操作符的重载,我们可以让任意类 都能 ++自加,且返回一个想要的对象。操作符执行的逻辑,完全看我们如何去设计。

分类

按元素级别

  • 一元
表达式对应函数
+aa.unaryPlus()
-aa.unaryMinus()
!aa.not()
a++a.inc()
a–a.dec()
  • 二元
表达式对应函数
a+ba.plus(b)
a-ba.minus(b)
a*ba.times(b)
a/ba.div(b)
a%ba.rem(b)
a…ba.range(b)
a in bb.contains(a)
a !in b!b.contains(a)
a[i]a.get(i)
a[i,j]a.get(i,j)
a[i_1,…,i_n]a.get(i_1,…,i_n)
a[i]=ba.set(i,b)
a[i,j]=ba.set(i,j,b)
a[i_1,…,i_n]=ba.set(i_1,…,i_j,b)
a()a.invoke()
a(i)a.invoke(i)
a(i,j)a.invoke(i,j)
a(i_1,…,i_n)a.invoke(i_1,…,i_n)
a+=ba.plusAssign(b)
a-=ba.minusAssign(b)
a*=ba.timesAssign(b)
a/=ba.divAssign(b)
a%=ba.modAssign(b)
a > ba.compareTo(b)>0
a < ba.compareTo(b)<0
a>=ba.compareTo(b)>=0
a<=ba.compareTo(b)<=0

按实现方式

  • 成员函数

  • 扩展函数

栗子

看到上面的一大堆,肯定有点懵,看个例子解决疑问。上面我用两种维度来对操作符重载进行了分类,那么,先试试:成员函数的方式来重载一个一元操作符

class A(i: Int, j: Int) {
var i: Int = i
var j: Int = j
/**

  • 重载++操作
    */
    operator fun inc(): A {
    return A(i++, j++)
    }
    override fun toString(): String {
    return “[i= i , j = i , j= i,j=j]”
    }
    }

如上代码,注意看:

operator fun inc(): A {

最后

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

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
转存中…(img-H4S5N0c7-1715678213790)]

[外链图片转存中…(img-UxbtKz1V-1715678213791)]

[外链图片转存中…(img-eSFtSCcN-1715678213792)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值