【Android】FlowBinding: 使用Coroutine Flow打造响应式UI

105 篇文章 30 订阅
10 篇文章 1 订阅

在这里插入图片描述

RxBinding


如今的Android开发中越来越多地开始引进MVI、Redux、单向数据流等概念,力求实现像react等前端框架那样的响应式UI开发体验。

除了彻底转向Jetpack Compose那样的激进方案外,客户端也有一些因地制宜的方案,比如RxBinding,通过RxJava与Android View的配合,用Observable替代OnClickListener,从而更高效地实现基于事件驱动的UI开发。

findViewById<Button>(R.id.button).clicks().subscribe {
    // handle button clicked
}

FlowBinding


kotlinx.coroutines 从1.3 开始增加了Flow库,可以在CoroutineScope中响应式地处理流式数据,堪称协程版的RxJava。相应的出现了很多RxJava=>Flow的优秀项目,今天介绍的FlowBinding就是其中一个:Flow版的RxBinding。

// Platform bindings
implementation "io.github.reactivecircus.flowbinding:flowbinding-android:${flowbinding_version}"
// AndroidX bindings
implementation "io.github.reactivecircus.flowbinding:flowbinding-appcompat:${flowbinding_version}"
implementation "io.github.reactivecircus.flowbinding:flowbinding-core:${flowbinding_version}"
implementation "io.github.reactivecircus.flowbinding:flowbinding-drawerlayout:${flowbinding_version}"
implementation "io.github.reactivecircus.flowbinding:flowbinding-navigation:${flowbinding_version}"
implementation "io.github.reactivecircus.flowbinding:flowbinding-recyclerview:${flowbinding_version}"
implementation "io.github.reactivecircus.flowbinding:flowbinding-swiperefreshlayout:${flowbinding_version}"
implementation "io.github.reactivecircus.flowbinding:flowbinding-viewpager2:${flowbinding_version}"
// Material Components bindings
implementation "io.github.reactivecircus.flowbinding:flowbinding-material:${flowbinding_version}"

除了Android标准控件以为,FlowBinding也支持各种AndroidX中的控件以及Material控件

使用方法

在CoroutineScope中监听OnClick事件:

uiScope.launch {
    findViewById<Button>(R.id.button)
        .clicks() // this returns a Flow<Unit>
        .collect {
            // handle button clicked
        }
}

kotlinx-coroutines-core中提供了launchIn(scope) ,可以简化 scope.launch { flow.collect() }的写法:

findViewById<Button>(R.id.button)
    .clicks() // binding API available in flowbinding-android
    .onEach {
        // handle button clicked
    }
    .launchIn(uiScope)

上面例子中的uiScope代表与Activity/Fragment的Lifecycle一致CoroutineScope,有效避免内存泄漏。

实际开发中可以使用androidx.lifecycle:lifecycle-runtime-ktx:2.2.0 提供的扩展属性LifecycleOwner.lifecycleScope: LifecycleCoroutineScope ,可以在lifecycle的onDestroy的时候,取消其Scope内的协程:

class ExampleActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_example)
        findViewById<Button>(R.id.button)
            .clicks()
            .onEach {
                // handle button clicked
            }
            .launchIn(lifecycleScope) // provided by lifecycle-runtime-ktx 
            
    }
}

实现原理

实现原理比较简单:

scope.launch {
    findViewById<Button>(R.id.button)
        .clicks() // this returns a Flow<Unit>
        .collect {
            // handle button clicked
        }
}

通过callbackFlow,可以将一个callback转成Flow,实现上面的clicks()方法

fun View.clicks(): Flow<Unit> = callbackFlow 
    val listener = View.OnClickListener {
        offer(Unit)
    }
    setOnClickListener(listener)
    awaitClose { setOnClickListener(null) }
}

awaitClose{}会在flow结束时执行,所以可以在此处进行反注册
offer()将数据发射到Flow内部使用的SendChannel,但是如果Flow已关闭,可能会抛出异常,所以可以增加对异常的捕获处理

fun <E> SendChannel<E>.safeOffer(value: E) = !isClosedForSend && try {
    offer(value)
} catch (e: CancellationException) {
    false
}

整段代码如下:
在这里插入图片描述

最后


FlowBinding借助Coroutine Flow打造了一套响应式UI的工具库,配合LifecycleScope实现自动注销避免内存泄漏。在Kotlin大行其道的今天,建议使用FlowBinding替代RxBinding,并以此为契机发掘更多地使用Flow替代RxJava的使用场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fundroid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值