关于Kotlin协程的一些应用_kotlin 协程在其他地方使用(1)

看方法名就知道了,尝试偷取任务,这,协程你是真的骚。

逻辑为:如果自己的本地队列没任务了,自己对应的全局队列也没有任务了,去其他的Work里面找任务执行。
这就是任务偷取机制。这也是Kotlin协程与Java线程池有所区别的最大两个点。
二、Kotlin协程如何去除回调
为什么讲到这个,是因为真的很多人并不知道这一点,把Kotlin写出了Java的感觉,在协程里面还到处搞一些回调/高阶函数之类的,破坏了协程的作用域。
协程的优雅之处就是把异步调用变成类似同步的效果,去除了回调的逻辑,看起来也更容易理解,也不会破坏协程的作用域。
suspendCoroutine:
例如我们写一个接口回调:
interface OnSingleMethodCallback {
fun onValueCallback(value: String)
}
使用回调:
fun runMethodTask(callback: OnSingleMethodCallback) {
Thread {
Thread.sleep(1000)
callback.onValueCallback(“abc”)
}.start()
}
fun runMethodTask(){
runMethodTask(object : OnSingleMethodCallback {
override fun onValueCallback(value: String) {
YYLogUtils.w(“value:$value”)
}
})
}
如果我们使用 suspendCoroutine:
suspend fun runMethodTaskWithSuspend(): String {
return suspendCoroutine { continuation ->
runMethodTask(object : OnSingleMethodCallback {
override fun onValueCallback(value: String) {
continuation.resume(value)
}
})
}
}
但是我们也需要注意,这是协程的写法,方法也标明了 suspend ,所以只能在协程中使用。
如果是一个网络请求有成功与失败的回调,那么我们也能使用 suspendCancellableCoroutine 来达到效果:
interface ICallBack {
fun onSuccess(data: String)
fun onFailure(t: Throwable)
}
private fun request(callback: ICallBack) {
thread {
try {
callback.onSuccess(“success”)
} catch (e: Exception) {
callback.onFailure(e)
}
}
}
private fun requestDefault() {
request(object : ICallBack {
override fun onSuccess(data: String) {
// …
}
override fun onFailure(t: Throwable) {
// …
}
})
}
如果使用 suspendCancellableCoroutine 的话就变成这样:
private suspend fun requestWithSuspend(): String {
return suspendCancellableCoroutine { cancellableContinuation->
request(object : ICallBack {
override fun onSuccess(data: String) {
cancellableContinuation.resume(data)
}
override fun onFailure(t: Throwable) {
cancellableContinuation.resumeWithException(t)
}
})
}
}
同样需要注意是,这是协程的写法,方法也标明了 suspend ,所以只能在协程中使用。
其实为什么Retrofit的请求看似把异步的网络请求用成了同步一样,Retrofit的内部也是同样的处理。
Retrofit最终的处理逻辑在此:KotlinExtensions.awaitResponse

所以我们照着Retrofit学就行了。
三、Kotlin协程分发器
有没有同学全部用 Dispatchers.IO 切换线程调度的。
Dispatchers.IO / Dispatchers.Default 的异同:
两者都是协程分发器,Dispatchers.IO 侧重于任务本身是阻塞型的,比如文件、数据库、网络等操作等。并不那么占用CPU
而Dispatchers.Default 则偏向那些可能会长时间占用CPU的任务。比如人脸特征提取,图片压缩处理,视频的合成等。
他们的线程池的实现也是不同的
协程线程池在设计的时候,针对两者在线程的调度策略上有所不同。
所有任务分成纯CPU任务和非纯CPU任务两种,对应着核心线程和非核心线程。
入队的逻辑是 Dispatchers.IO 的任务放入 globalBlockingQueue 队列,而 Dispatchers.Default 的任务放入的是 globalBlockingQueue 队列。
所有线程在执行前都先尝试成为核心线程,核心线程可以从两种任务中任意选择执行,非核心线程只能执行非纯CPU任务。核心线程如果选择执行非纯CPU任务会变成非核心线程。
所以真的有人从来没用过 Dispatchers.Default 吗?
四、使用协程有什么好处?怎么用?
看到过网上的一些Java线程池比协程线程池执行逻辑更快的文章,其实意义不大,协程最大的优势是会更加的方便,可以很方便的把一些碎片化的方法加入协程,同时它可以去掉回调地狱还能更加方便的实现并发与排队执行的效果。
比如这样的一个场景,在主线程计算薪水,我们根据时薪与工作时长计算总共的薪水,内部有复杂的判断,是否是签约员工,是否迟到了,迟到了扣钱,扣除五险一金,连续工作的奖励,推荐的奖励,顾客打赏,等等一系列的复杂逻辑,我们就可以随意加入协程中。
private fun calculateSalary(): String {
// 省略100行代码
return “1000”
}
private suspend fun calculateSalary2() = withContext(Dispatchers.Default) {
// 省略100行代码
“2000”
}
private suspend fun calculateSalary3() = coroutineScope {
// 省略100行代码
“3000”
}
下面看看代码的优化:
class CalculateFaceUtil private constructor() : CoroutineScope by MainScope() {
//… 单例
/**
* 计算并找到最匹对的人脸信息
*
* 使用协程异步的并发的双端遍历查询最大值
*/
fun getTopFace(
list: List,
faceEngine: FaceEngine,
faceFeature: FaceFeature,
action: (similar: Float, index: Int) -> Unit
) {
// 、、、其他逻辑
val middlePosition = list.size / 2
launch(Dispatchers.IO) {
val topface1 = async {
val tempFaceFeature = FaceFeature()
val faceSimilar = FaceSimilar()
var maxSimilar = 0f
var maxSimilarIndex = -1
for (i in 0 until middlePosition) {
tempFaceFeature.featureData = list[i].featureData
//调用SDK比对两个 FaceFeature 人脸特征,返回相似度
faceEngine.compareFaceFeature(faceFeature, tempFaceFeature, faceSimilar)
//拿到相似度的对象,获取得分(每一次都会全部遍历,如果有相同的图片还是会取到最后的)
if (faceSimilar.score > maxSimilar) {
maxSimilar = faceSimilar.score
maxSimilarIndex = i
}
}
TopFace(maxSimilar, maxSimilarIndex)
}
val topface2 = async {
val tempFaceFeature = FaceFeature()
val faceSimilar = FaceSimilar()
var maxSimilar = 0f
var maxSimilarIndex = -1
for (i in middlePosition until list.size) {
tempFaceFeature.featureData = list[i].featureData
//调用SDK比对两个 FaceFeature 人脸特征,返回相似度
faceEngine.compareFaceFeature(faceFeature, tempFaceFeature, faceSimilar)
//拿到相似度的对象,获取得分(每一次都会全部遍历,如果有相同的图片还是会取到最后的)
if (faceSimilar.score > maxSimilar) {
maxSimilar = faceSimilar.score
maxSimilarIndex = i
}
}
TopFace(maxSimilar, maxSimilarIndex)
}
//并发查找并找到最大值
val face1 = topface1.await()
val face2 = topface2.await()
if (face1 != null && face2 != null) {
//回调到主线程
withContext(Dispatchers.Main) {
//优先返回后面的数据
if (face2.similar > face1.similar) {
action(face2.similar, face2.index)
} else {
action(face1.similar, face1.index)
}
}
}
}
}
}
场景:ViewModel中调用这个工具类,查找较大集合中最匹配的人脸,使用头尾双端遍历找到最大值。
我们以这一个使用场景为例,逻辑没问题,但是协程的使用有优化的空间。
1.计算最好使用 Dispatchers.Default , 这是小问题。 2.viewModel中viewModelScope协程作用域中调用全局的协程作用域,这…感觉不太好,推荐使用下面的方式,继承父布局的协程作用域。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。


既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!**
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-IDpi7bGh-1712746524127)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
更多推荐


所有评论(0)