简单总结就是,主从关系:无法坑爹,爹可以坑儿子。协同关系:可以坑爹,可以坑儿子,互相坑。
如果是应用的话,主要掌握框架级别的使用即可,语言级别的支持api来源于标准库,写起来比较麻烦也非常难理解。
这里launch会进行一次调度 ,delay会进行一次调度,每次调度完成会执行一次resume, 最终协程体执行完毕会执行一次resume, 所以内部有n个挂起点的协程体会执行n+2次resume.
DEFAULT 立即开始调度 和 UNDISPATCHED 立即开始执行协程体,这两个含义的区别是 DEFAULT 只是立即启动协程执行可能是异步的,而后者是直接执行协程体中的代码了。LAZY 是先创建协程体,然后在未来的某个时刻才去启动执行。
UNDISPATCHED 立即开始执行协程体,如果遇到挂起点,就切回主流程了,后面的协程体继续执行在单独的调度器。
import kotlinx.coroutines.*
@ExperimentalCoroutinesApi
suspend fun main() {
println(“start”)
testDefaultMode()
// testAtomicMode()
// testLazyMode()
// testUNDISPATCHEDMode()
println(“finish”)
}
suspend fun testDefaultMode() {
val defaultMode = GlobalScope.launch(start = CoroutineStart.DEFAULT) {
println(“aaa”)
delay(3000)
println(“bbb”)
}
println(“222”)
defaultMode.join()
}
@ExperimentalCoroutinesApi
suspend fun testAtomicMode() {
val defaultMode = GlobalScope.launch(start = CoroutineStart.ATOMIC) {
println(“aaa”)
delay(3000)
println(“bbb”)
}
println(“222”)
defaultMode.join()
}
suspend fun testLazyMode() {
val defaultMode = GlobalScope.async(start = CoroutineStart.LAZY) {
println(“aaa”)
delay(3000)
println(“bbb”)
}
println(“222”)
defaultMode.await()
}
@ExperimentalCoroutinesApi
suspend fun testUNDISPATCHEDMode() {
val defaultMode = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
println(“aaa”)
delay(3000)
println(“bbb”)
}
println(“222”)
defaultMode.join()
}
Default和IO线程的区别,IO内部多了一个队列的维护
回调转协程的完整写法:
import com.bennyhuo.kotlin.coroutines.advanced.common.gitHubServiceApi
import kotlinx.coroutines.suspendCancellableCoroutine
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
suspend fun Call.await(): T = suspendCancellableCoroutine { //可取消
continuation ->
continuation.invokeOnCancellation {
cancel() //调用retrofit的取消方法
}
enqueue(object: Callback {
override fun onFailure(call: Call, t: Throwable) {
continuation.resumeWithException(t)
}
override fun onResponse(call: Call, response: Response) {
response.takeIf { it.isSuccessful }?.body()?.also {continuation.resume(it) }
?: continuation.resumeWithException(HttpException(response))
}
})
}
suspend fun main() {
val user = gitHubServiceApi.getUserCallback(“flycumt”).await()
println(user)
}
也可以不自己写,retrofit的api中本身有实现await()
方法,awaitResponse()
方法等。
CompletableFuture 添加回调的写法:
import com.bennyhuo.kotlin.coroutines.advanced.utils.log
import kotlinx.coroutines.suspendCancellableCoroutine
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
suspend fun main() {
val result = CompletableFuture.supplyAsync {
3
}.await()
log(result)
}
suspend fun CompletableFuture.await(): T {
if(isDone){
try {
return get()
} catch (e: ExecutionException) {
throw e.cause ?: e
}
}
return suspendCancellableCoroutine { //可取消
cancellableContinuation ->
cancellableContinuation.invokeOnCancellation {
cancel(true) //取消
}
whenComplete { value, throwable ->
if(throwable == null){
cancellableContinuation.resume(value)
} else {
cancellableContinuation.resumeWithException(throwable.cause ?: throwable)
}
}
}
}
CompletableFuture本身也有实现await()
方法。
模仿给Handler扩展添加可取消的支持:
suspend fun Handler.run(block: () -> T) = suspendCoroutine { continuation ->
post {
try {
continuation.resume(block())
} catch (e: Exception) {
continuation.resumeWithException(e)
}
}
}
suspend fun Handler.runDelay(delay: Long, block: () -> T) = suspendCancellableCoroutine { continuation ->
val message = Message.obtain(this) { //Message obtain(Handler h, Runnable callback)
try {
continuation.resume(block())
} catch (e: Exception){
continuation.resumeWithException(e)
}
}.also {
it.obj = continuation //message.obj
}
continuation.invokeOnCancellation {
removeCallbacksAndMessages(continuation) //通过Handler的removeCallbacksAndMessages方法来取消回调, 参数就是前面设置的message.obj的值
}
sendMessageDelayed(message, delay)
}
suspend fun main() {
Looper.prepareMainLooper()
GlobalScope.launch {
val handler = Handler(Looper.getMainLooper())
val result = handler.run { “Hello” }
val delayedResult = handler.runDelay(5000){ “World” }
log(result, delayedResult)
Looper.getMainLooper().quit()
}
Looper.loop()
}
这个例子的主要意图是,Hanlder可以通过定义扩展函数的方式来延时获取一些东西,比如Activity刚创建的时候,拿不到view的宽和高,就可以使用这种方法。
上面三个例子主要是针对可取消的写法,如果实际用,不用自己写,直接导库就行。
其中CONFLATED比较适合用于状态更新,比如进度条的进度,因为它总是只取最新的。
关闭后再发送会抛异常:
channel关闭后,channel中的数据仍然可以被接受,只有当channel中的数据消费完了,isClosedForReceive才为true.
suspend fun main() {
basics()
}
suspend fun basics() {
val channel = Channel(Channel.RENDEZVOUS)
// val channel = Channel(Channel.UNLIMITED)
// val channel = Channel(Channel.CONFLATED)
// val channel = Channel(Channel.BUFFERED)
// val channel = Channel(1)
//生产者 发
val producer = GlobalScope.launch {
for (i in 0…3) {
log(“sending”, i)
channel.send(i)
log(“sent”, i)
}
channel.close()
}
//消费者 收
val consumer = GlobalScope.launch {
while (!channel.isClosedForReceive) {
log(“receiving”)
val value = channel.receiveOrNull()
log(“received”, value)
}
}
producer.join()
consumer.join()
}
Channel(Channel.RENDEZVOUS ) 的方式是发一个收一个,边发边收,如果没有接受的,发送者会挂起等待,输出如下:
Channel(Channel.UNLIMITED ) 的方式是全部发送完毕,才会接收到,先发后收,发送者发送完就返回了,不管有没有接受者,输出如下:
Channel(Channel.CONFLATED ) 的方式是不管发了多少个,只能收到最后一个,也是发送完就返回了,不管有没有接受者,输出如下:
Channel(Channel.BUFFERED ) 的方式也是发送者发送完就返回了,不管有没有接受者,可以指定buffer大小,输出如下:
Channel(1) 的方式指定管道的容量大小,如果数据超过容量,发送者就会挂起等待,直到有接受者取走数据,发送者才发送下一批数据,
channel接受数据的时候可以直接当成迭代器使用:
suspend fun iterateChannel() {
val channel = Channel(Channel.UNLIMITED)
val producer = GlobalScope.launch {
for (i in 0…3) {
log(“sending”, i)
channel.send(i)
log(“sent”, i)
}
channel.close()
}
val consumer = GlobalScope.launch {
for (i in channel) {
log("received: ", i)
}
}
producer.join()
consumer.join()
}
suspend fun producer() {
val receiveChannel = GlobalScope.produce(capacity = Channel.UNLIMITED) {
for (i in 0…3) {
log(“sending”, i)
send(i)
log(“sent”, i)
}
}
val consumer = GlobalScope.launch {
for (i in receiveChannel) {
log("received: ", i)
}
}
consumer.join()
}
suspend fun consumer() {
val sendChannel = GlobalScope.actor(capacity = Channel.UNLIMITED) {
for (i in this) {
log("received: ", i)
}
}
val producer = GlobalScope.launch {
for (i in 0…3) {
log(“sending”, i)
sendChannel.send(i)
log(“sent”, i)
}
}
producer.join()
}
suspend fun broadcast() {
//下面几种都可以创建一个BroadcastChannel
//val broadcastChannel = BroadcastChannel(Channel.BUFFERED)
//val broadcastChannel = Channel(Channel.BUFFERED).broadcast()
val broadcastChannel = GlobalScope.broadcast {
for (i in 0…5) {
send(i)
}
}
//启动5个接受者,每个都能收到
List(5) { index ->
GlobalScope.launch {
val receiveChannel = broadcastChannel.openSubscription()
for (i in receiveChannel) {
log(“[#$index] received: $i”)
}
}
}.joinAll()
}
输出:
Task :ChannelsKt.main()
21:07:12:924 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 0
21:07:12:924 [DefaultDispatcher-worker-5 @coroutine#6] [#4] received: 0
21:07:12:924 [DefaultDispatcher-worker-1 @coroutine#3] [#1] received: 0
21:07:12:925 [DefaultDispatcher-worker-4 @coroutine#5] [#3] received: 0
21:07:12:925 [DefaultDispatcher-worker-2 @coroutine#4] [#2] received: 0
21:07:12:944 [DefaultDispatcher-worker-1 @coroutine#3] [#1] received: 1
21:07:12:943 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 1
21:07:12:943 [DefaultDispatcher-worker-5 @coroutine#6] [#4] received: 1
21:07:12:944 [DefaultDispatcher-worker-4 @coroutine#5] [#3] received: 1
21:07:12:945 [DefaultDispatcher-worker-2 @coroutine#4] [#2] received: 1
21:07:12:945 [DefaultDispatcher-worker-2 @coroutine#4] [#2] received: 2
21:07:12:945 [DefaultDispatcher-worker-8 @coroutine#3] [#1] received: 2
21:07:12:945 [DefaultDispatcher-worker-8 @coroutine#3] [#1] received: 3
21:07:12:945 [DefaultDispatcher-worker-7 @coroutine#4] [#2] received: 3
21:07:12:945 [DefaultDispatcher-worker-2 @coroutine#6] [#4] received: 2
21:07:12:946 [DefaultDispatcher-worker-2 @coroutine#6] [#4] received: 3
21:07:12:946 [DefaultDispatcher-worker-8 @coroutine#5] [#3] received: 2
21:07:12:946 [DefaultDispatcher-worker-8 @coroutine#5] [#3] received: 3
21:07:12:946 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 2
21:07:12:946 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 3
21:07:12:946 [DefaultDispatcher-worker-1 @coroutine#3] [#1] received: 4
21:07:12:946 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 4
21:07:12:946 [DefaultDispatcher-worker-1 @coroutine#6] [#4] received: 4
21:07:12:947 [DefaultDispatcher-worker-6 @coroutine#5] [#3] received: 4
21:07:12:947 [DefaultDispatcher-worker-6 @coroutine#5] [#3] received: 5
21:07:12:947 [DefaultDispatcher-worker-2 @coroutine#3] [#1] received: 5
21:07:12:947 [DefaultDispatcher-worker-6 @coroutine#2] [#0] received: 5
21:07:12:947 [DefaultDispatcher-worker-2 @coroutine#6] [#4] received: 5
21:07:12:947 [DefaultDispatcher-worker-3 @coroutine#4] [#2] received: 4
21:07:12:947 [DefaultDispatcher-worker-3 @coroutine#4] [#2] received: 5
Select的使用场景是多个协程异步执行时,获取最先结束的那个协程结果返回,比如加载图片时,可能从网络获取,也可能从本地获取,这两种可能同时异步执行,使用Select就会优先获取返回比较快的本地结果展示,然后我们再去获取网络最新的更新即可。
使用例子:
val localDir = File(“localCache”).also { it.mkdirs() }
val gson = Gson()
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
尾声
评论里面有些同学有疑问关于如何学习material design控件,我的建议是去GitHub搜,有很多同行给的例子,这些栗子足够入门。
有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。
一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
尾声
评论里面有些同学有疑问关于如何学习material design控件,我的建议是去GitHub搜,有很多同行给的例子,这些栗子足够入门。
有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。
一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!