Kotlin callbackFlow

243 阅读2分钟

Kotlin callbackFlow

  • callbackFlow 可以将普通的 Callback 回调转换成 Flow 数据流,特别是文本搜索框监听、网络状态监听和传感器数据刷新等场景
  • callbackFlow 是冷流,底层是 Channel 实现的
  • awaitClose 方法是必须配合使用的,可以保证资源被及时释放,避免内存泄漏
  • 和 suspendCancelableCoroutine 应用场景有些相似,总的来说 callbackFlow 更适用于多次回调的场景,而 suspendCancelableCoroutine 更适用于单次回调

将回调转换成数据流

interface Callback {
    fun onSuccess(data: String)
    fun onFailure(e: Exception)
}
fun fetchData(callback: Callback) {
    thread {
        //模拟耗时然后返回数据
        Thread.sleep(3000)
        val value = Random.nextInt()
        if (value % 2 == 0) {
            callback.onSuccess("data_$value")
        } else {
            callback.onFailure(Exception("error_$value"))
        }
    }
}
fun fetchDataByCallbackFlow(): Flow<String> = callbackFlow {
    val callback = object : Callback {
        override fun onSuccess(data: String) {
            //发送数据到 Flow 中(trySend 尝试往 Channel 中添加元素,如果 Channel 已经满了,那么就立刻会返回失败)
            val result =  trySend(data)
            //trySendBlocking 尝试往 Channel 中添加元素,当 Channel 已满的时候,会阻塞等待直到有空闲以后添加再返回成功或者 Channel 关闭
            //val result =  trySendBlocking(data)
            val isSuccess = result.isSuccess
            println("trySend: isSuccess=${isSuccess}")
        }

        override fun onFailure(e: Exception) {
            //出现异常时关闭 Channel
            val result = close(e)
            println("close: result=${result}")
        }
    }
    //注册
    fetchData(callback)
    //awaitClose 是必须的,否则会报异常
    awaitClose {
        //流的收集被取消时、协程被 cancel 取消时或显式调用 Channel#close 方法时 awaitClose 才会被调用
        //可以取消注册回调
        println("awaitClose close")
    }
}
//使用
launch {
    try {
        fetchDataByCallbackFlow()
            .collect { data ->
                println("fetchDataByCallbackFlow: data=$data")
            }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

也可以这么用

interface CallbackWithComplete {
    fun onSuccess(data: String)
    fun onFailure(e: Exception)
    fun onComplete()
}
fun fetchData(callback: CallbackWithComplete) {
    thread {
        //模拟耗时然后返回数据
        Thread.sleep(3000)
        val value = Random.nextInt()
        if (value % 2 == 0) {
            callback.onSuccess("data_$value")
        } else {
            callback.onFailure(Exception("error_$value"))
        }
        callback.onComplete()
    }
}
fun fetchDataByCallbackFlow(): Flow<String> = callbackFlow {
    val callback = object : CallbackWithComplete {
        override fun onSuccess(data: String) {
            //发送数据到 Flow 中(trySend 尝试往 Channel 中添加元素,如果 Channel 已经满了,那么就立刻会返回失败)
            val result =  trySend(data)
            //trySendBlocking 尝试往 Channel 中添加元素,当 Channel 已满的时候,会阻塞等待直到有空闲以后添加再返回成功或者 Channel 关闭
//            val result =  trySendBlocking(data)
            val isSuccess = result.isSuccess
            println("trySend: isSuccess=${isSuccess}")
        }

        override fun onFailure(e: Exception) {
            //取消协程
            cancel("callback error", e)
        }

        override fun onComplete() {
            //完成后关闭 Channel
            val result = close()
            println("close: result=${result}")
        }
    }
    //注册
    fetchData(callback)
    //awaitClose 是必须的,否则会报异常
    awaitClose {
        //流的收集被取消时、协程被 cancel 取消时或显式调用 Channel#close 方法时 awaitClose 才会被调用
        //可以取消注册回调
        println("awaitClose close")
    }
}

常见使用场景

//比如 EditText 输入框的搜索逻辑
fun TextView.textWatcherFlow(): Flow<String> {
    return callbackFlow {
        val textWatcher = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                s?.let {
                    trySend(s.toString())
                }
            }
            override fun beforeTextChanged(
                s: CharSequence?,
                start: Int,
                count: Int,
                after: Int
            ) { 
            
            }
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { 

            }
        }
        //TextView#addTextChangedListener
        addTextChangedListener(textWatcher)
        //awaitClose 方法是必须的,用于处理资源的关闭
        awaitClose {
            //通常在这里移除取消回调注册
            //TextView#removeTextChangedListener
            removeTextChangedListener(textWatcher)
        }
    }
}