// Get the Continuation object of the coroutine that it’s running this suspend function
suspendCoroutineUninterceptedOrReturn { uCont ->
// Take over the control of the coroutine. The Continuation’s been
// intercepted and it follows the CancellableContinuationImpl lifecycle now
val cancellable = CancellableContinuationImpl(uCont.intercepted(), …)
/* … */
// Call block of code with the cancellable continuation
block(cancellable)
// Either suspend the coroutine and wait for the Continuation to be resumed
// manually in block
or return a result if block
has finished executing
cancellable.getResult()
}
Streaming data
如果我们想获取多个数据流(使用NetAPI.getDataList函数),我们就需要使用Flow创建一个数据流。理想的API应该是这样的。
fun NetAPI.getDataListFlow(): Flow
要将基于回调的流媒体API转换为Flow,我们需要使用创建Flow的callbackFlow构建器。在callbackFlow lambda中,我们处于Coroutine的上下文中,因此,可以调用suspend函数。与flow构建器不同,callbackFlow允许通过send函数从不同CoroutineContext发出值,或者通过offer函数在协程外发出值。
通常情况下,使用callbackFlow的流适配器遵循这三个通用步骤。
-
创建回调,使用offer将元素添加到流中。
-
注册该回调。
-
等待消费者取消循环程序并取消对回调的注册。
示例代码如下所示。
// 向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 }
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://img-blog.csdnimg.cn/img_convert/6f6b2afae406d2f04ab59ff9d311bb90.jpeg)
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
下图是我进阶学习所积累的历年腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节
整理不易,望各位看官老爷点个关注转发,谢谢!祝大家都能得到自己心仪工作。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
下图是我进阶学习所积累的历年腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节
[外链图片转存中…(img-oNDkdAM7-1712378724274)]
整理不易,望各位看官老爷点个关注转发,谢谢!祝大家都能得到自己心仪工作。