再谈协程之Callback写出协程范儿

示例代码如下所示。

// 向consumer发送Data updates

fun NetAPI.getDataListFlow() = callbackFlow {

// 当前会在一个协程作用域中创建一个新的Flow

// 1. 创建回调,使用offer将元素添加到流中

val callback = object : NetCallback() {

override fun success(result: Result?) {

result ?: return // Ignore null responses

for (data in result.datas) {

try {

offer(data) // 将元素添加至flow

} catch (t: Throwable) {

// 异常处理

}

}

}

}

// 2. 注册该回调,从而获取数据流

requestDataUpdates(callback).addOnFailureListener { e ->

close(e) // 异常时close

}

// 3. 等待消费者取消循环程序并取消对回调的注册,这样会suspend当前协程,直到这个flow被关闭

awaitClose {

// 移除监听

removeLocationUpdates(callback)

}

}

callbackFlow背后的原理

在协程内部,callbackFlow会使用channel,它在概念上与阻塞队列非常相似。channel都有容量配置,限定了可缓冲元素数的上限。

在callbackFlow中所创建channel的默认容量为64个元素,当你尝试向已经满的channel添加新元素时,send函数会将数据提供方挂起,直到新元素有空间能加入channel为止,而offer不会将相关元素添加到channel中,并会立即返回false。

awaitClose背后的原理

awaitClose的实现原理其实和suspendCancellableCoroutine是一样的,参考下下面的代码中的注释。

public suspend fun ProducerScope<*>.awaitClose(block: () -> Unit = {}) {

try {

// Suspend the coroutine with a cancellable continuation

suspendCancellableCoroutine { cont ->

// Suspend forever and resume the coroutine successfully only

// when the Flow/Channel is closed

invokeOnClose { cont.resume(Unit) }

}

} finally {

// Always execute caller’s clean up code

block()

}

}

有啥用?


将基于回调的API转换为数据流,这玩意儿到底有什么用呢?我们拿最常用的View.setOnClickListener来看下,它既可以看作是一个One-shot的场景,也可以看作是数据流的场景。

我们先把它改写成suspendCancellableCoroutine形式,代码如下所示。

suspend fun View.awaitClick(block: () -> Unit): View = suspendCancellableCoroutine { continuation ->

setOnClickListener { view ->

if (view == null) {

continuation.resumeWithException(Exception(“error”))

} else {

block()

continuation.resume(view)

}

}

}

使用:

lifecycleScope.launch {

binding.test.awaitClick {

Toast.makeText(this@MainActivity, “loading”, Toast.LENGTH_LONG).show()

}

}

嗯,有点一言难尽的感觉,就差脱裤子放屁了。我们再把它改成数据流的场景。

fun View.clickFlow(): Flow {

return callbackFlow {

setOnClickListener {

trySend(it) // offer函数被Deprecated了,使用trySend替代

}

awaitClose { setOnClickListener(null) }

}

}

使用:

lifecycleScope.launch {

binding.test.clickFlow().collect {

Toast.makeText(this@MainActivity, “loading”, Toast.LENGTH_LONG).show()

}

}

好了,屁是完全放出来了。

可以发现,这种场景下,强行硬套这种模式,其实并没有什么卵用,反而会让别人觉得你是个智障。

那么到底什么场景需要使用呢?我们可以想想,为什么需要Callbback。

大部分Callback hell的场景,都是异步请求,也就是带阻塞的那种,或者就是数据流式的数据产出,所以这种仅仅是调用个闭包的回调,其实不能叫回调,它只是一个lambda,所以,我们再来看一个例子。

现在有一个TextView,显示来自一个Edittext的输入内容。这样一个场景就是一个明确的数据流场景,主要是利用Edittext的TextWatcher中的afterTextChanged回调,我们将它改写成Flow形式,代码如下所示。

fun EditText.afterTextChangedFlow(): Flow<Editable?> {

return callbackFlow {

val watcher = object : TextWatcher {

override fun afterTextChanged(s: Editable?) {

trySend(s)

}

override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

}

addTextChangedListener(watcher)

awaitClose { removeTextChangedListener(watcher) }

}

}

使用:

lifecycleScope.launch {

with(binding) {

test.afterTextChangedFlow().collect { show.text = it }

}

}

有点意思了,我没写回调,但是也拿到了数据流,嗯,其实有点「强行可以」的感觉。

但是,一旦这里变成了Flow,这就变得很有味道了,这可是Flow啊,我们可以利用Flow那么多的操作符,做很多有意思的事情了。

举个例子,我们可以对输入框做限流,这个场景很常见,例如搜索,用户输入的内容会自动搜索,但是又不能一输入内容就搜索,这样会产生大量的无效搜索内容,所以,这个场景也有个专有名词——输入框防抖。

之前在处理类似的需求时,大部分都是采用RxJava的方式,但现在,我们有了Flow,可以在满足协程范API的场景下,依然完成这个功能。

我们增加一下debounce即可。

lifecycleScope.launch {

with(binding) {

test.afterTextChangedFlow()

.buffer(Channel.CONFLATED)

.debounce(300)

.collect {

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司21年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司21年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-Gb8Ba6TW-1720126849623)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值