Convert any callback API into a self cleaning Flow.
将所有回调API转换为自清除流。
Kotlin Flows are amazing. Not only do they provide a non-blocking asynchronous way to deal with sequences of values, but they provide a clean and powerful API for dealing with event driven programming. Flows are easy to clean up to prevent leaks. They provide many functions and operators to deal with a myriad of scenarios and requirements. With an experimental function they are super easy to create around callback APIs.
Kotlin Flows很棒。 它们不仅提供了一种处理值序列的非阻塞异步方式,而且还提供了一种用于处理事件驱动程序的干净而强大的API。 流量易于清理以防止泄漏。 他们提供了许多功能和操作员来应对各种场景和要求。 通过实验功能,它们非常容易围绕回调API创建。
The Jetpack team added CoroutineScope
support to the lifecycle in lifecycle-runtime-ktx
version 2.2.0-alpha01
. This allowed callers to launch coroutines in created, started, and resumed states that automatically cancelled them in the appropriate complementary state. For example the following Flow
will be collected while the activity is started and stop when the onStop
method is called:
Jetpack团队在lifecycle-runtime-ktx
版本2.2.0-alpha01
向生命周期添加了CoroutineScope
支持。 这使调用者可以在创建,启动和恢复状态下启动协程,并在适当的互补状态下自动取消协程。 例如,将在活动开始时收集以下Flow
并在onStop
方法时停止以下Flow
:
lifecycleScope.launchWhenStarted {
viewModel.getFlow().collect {
// Do something with value
}
}
Additionally they added a viewModelScope
in lifecycle-viewmodel-ktx
version 2.1.0-beta01
. This scope cancels coroutines when ViewModel.onCleared()
is called. By properly using these scopes, we avoid having to remember to stop collecting from our flows, and avoid leaking long running processes when we no longer need them.
另外,他们在lifecycle-viewmodel-ktx
版本2.1.0-beta01
添加了viewModelScope
。 调用ViewModel.onCleared()
时,此作用域取消协程。 通过正确使用这些作用域,我们不必记住必须停止从流中收集数据,并避免在不再需要长时间运行的流程时泄漏它们。
Many times when dealing with event based programming, developers want to be selective about what events they deal with. Kotlin Flows provide a great set of APIs to help simplify that process and avoid cluttering our collection code. Here are a few of my favorites:
很多时候,在处理基于事件的编程时,开发人员都希望对处理的事件保持选择性。 Kotlin Flows提供了很多API,可帮助简化该过程并避免使我们的收集代码混乱。 以下是一些我的最爱:
fitlerIsInstance
— include values that are of a given type and cast them.fitlerIsInstance
-包括那些给定类型和铸造他们的值。filterNotNull
— include values that are not nullfilterNotNull
—包含不为null的值debounce
— filter out values by the newer values within a given timeout. Limiting rate of values so we don’t have to process unimportant, superseded ones.debounce
—在给定的超时时间内通过较新的值过滤掉值。 限制值的速率,因此我们不必处理不重要的,被取代的值。mapLatest
— transform the latest value by the provided transform function.mapLatest
—通过提供的转换函数转换最新值。flatMapLatest
— like the map operator above but returns the values from the new flow returned by the transform function.flatMapLatest
—与上面的地图运算符一样,但是从transform函数返回的新流中返回值。combine
— combine the most recently emitted values of multiple flows.combine
-合并多个流的最新发出的值。distinctUntilChanged
— filter out subsequent repetition of values.distinctUntilChanged
过滤出后续的值重复。buffer
— buffer value emission to handle back pressure.buffer
—发出缓冲值以处理背压。flowOn
— change the context on which the preceding operations are executed.flowOn
—更改在其上执行前面的操作的上下文。
In Android, there are many libraries and systems that we would love to get a flow of events or values that we can easily operate on in the background. Version 1.3.0-M1
of coroutines added a new experimental flow builder called callbackFlow
. This builder provides a simple and easy way to create a flow for callback based APIs. The key piece is the function awaitClose
which lets us remove/unregister our callbacks when the flow is closed.
在Android中,我们希望获得许多库和系统,以便获得可以在后台轻松进行操作的事件或值的流程。 协程的1.3.0-M1
版本添加了一个新的实验流程构建器,称为callbackFlow
。 此构建器提供了一种简单的方法来为基于回调的API创建流。 关键是函数awaitClose
,它使我们可以在流程关闭时删除/注销回调。
A simple example is using a TextWatcher
to perform a search while the user is typing. Now we don’t want to search while the user is frantically typing so we can combine the callbackFlow
, buffer
, and the debounce
functions to make that simple:
一个简单的示例是在用户键入时使用TextWatcher
进行搜索。 现在我们不想在用户疯狂输入时搜索,因此我们可以结合使用callbackFlow
, buffer
和debounce
函数来简化操作:
fun TextView.textWatcherFlow(): Flow<CharSequence?> = callbackFlow<CharSequence?> {
val textWatcher = object: TextWatcher {
override fun afterTextChanged(s: Editable?) {
sendBlocking(s)
}
// Other callback mehthods
}
addTextChangedListener(textWatcher)
awaitClose { removeTextChangedListener(textWatcher) }
}.buffer(Channel.CONFLATED)
.debounce(300L)
The first step inside the callbackFlow
is to create ourTextWatcher
. Inside that callback we use sendBlocking
to pass the values we care about to the flow. Next we add the callback to the TextView
followed immediately by awaitClose
. Inside that block we remove the TextWatcher
, so we are fully cleaning up our listener. Now, sendBlocking
can block, as the name indicates, but by using buffer
we can avoid that from happening. In this case we want to use CONFLATED
to only send the latest value if there are conflicts. Lastly we use debounce
to wait for a lull of 300 ms in the typing before emitting the last value. We will keep getting new values at a rate of 300 ms or slower until the scope we launched with collect
in is cancelled.
callbackFlow
内部的第一步是创建TextWatcher
。 在该回调内部,我们使用sendBlocking
将我们关心的值传递给流。 接下来,我们将回调添加到TextView
紧接着是awaitClose
。 在该块内,我们删除了TextWatcher
,因此我们正在完全清理侦听器。 现在, sendBlocking
, sendBlocking
可以阻止,但是通过使用buffer
我们可以避免这种情况的发生。 在这种情况下,我们希望使用CONFLATED
仅在发生冲突时发送最新值。 最后,在发出最后一个值之前,我们使用debounce
来等待300 ms的暂停。 在取消使用collect
in启动的作用域之前,我们将以300毫秒或更慢的速度获取新值。
lifecycleScope.launchWhenStarted {
textView.textWatcherFlow().collect {
// pass value to viewModel to perform search
}
}
But wait, there’s more
但是等等,还有更多
What if we want to stop collecting mid scope? All of the launch functions: launch
, launchWhenCreated
, launchWhenStarted
, launchWhenResumed
return a coroutine Job
. A job gives us a handle on the coroutine so we can cancel it when necessary as follows:
如果我们要停止收集中视镜怎么办? 所有启动功能: launch
, launchWhenCreated
, launchWhenStarted
, launchWhenResumed
返回协程Job
。 一项工作为我们提供了协程的处理,因此我们可以在必要时取消它,如下所示:
val job = lifecycleScope.launchWhenStarted {
textView.textWatcherFlow().collect {
// pass value to viewModel to perform search
}
}job.cancel()
Cancelling a Job
will cancel the Flow
and trigger the awaitClose
block cleaning up our callback.
取消Job
将取消Flow
并触发awaitClose
块以清理回调。
Flows are powerful and callbackFlow
makes it super easy to turn any callback based API into a self cleaning flow.
流程功能强大, callbackFlow
使将任何基于回调的API转换为自清洁流程变得非常容易。
翻译自: https://medium.com/swlh/callback-flows-in-android-d2c6ed5bc488