美文网首页
Android 利用 Kotlin callbackFlow优雅

Android 利用 Kotlin callbackFlow优雅

作者: 云飞扬1 | 来源:发表于2022-08-26 17:08 被阅读0次

    1. 需求场景

    几乎大部分人都使用过下面的场景:使用百度等搜索引擎时,输入一个字符后会自动联想出相关的搜索关键字;在淘宝、京东等APP搜索商品时,会自动帮你联想出相关的商品。这在很大程度上提高了用户体验,快速引导用户到达搜索结果页。

    所以我们总结出同类型的需求,有一个搜索输入框,用户可以随意输入任何字符,然后前端根据用户的输入给出联想提示信息。

    2. 实现方案

    一般前端实现起来也不复杂,前端监听输入框文本的变化,然后根据输入内容从本地或者调用接口从服务端查询相关联数据,然后在前端展示结果即可。虽然看起来很简单,但是要比较完美地实现,还是有很多需要考虑的:

    1. 用户输入文本的速度是随机的,如果监听文本变化之后立马去查询,可能会过多地发起查询,浪费系统资源。例如用户本意要输入"abcde",那么每输入一个字符都会触发查询,会触发 5 次,其实用户可能只想得到 "abcde" 的查询结果;
    2. 查询是异步的,异步返回的时机是不确定的,可能造成当前返回的异步查询结果与当前输入框里的文本不一致。例如用户输入"ab",先后触发关键字 "a"、"ab" 的查询,结果 "a" 的查询结果后返回,最后导致显示的是 "a" 的查询结果;

    所以一个优雅的方案我觉得应该包含:

    1. 防抖限流;
    2. 自动取消无效查询;

    以前要实现这些效果,监听输入框变化时,会通过计时以及延时任务等,在限定时间段内只触发一次,写起来也很麻烦。

    3. 使用 Kotlin callbackFlow

    Kotlin Flow 支持防抖、背压等特性,利用 callbackFlow 可以将输入框的文本变化包装成一个 Flow,然后来实现这个需求。

    将 EditText 的输入文本变化包装成 Flow:

    private fun textChangeFlow(editText: EditText): Flow<String>  {
        return callbackFlow {
            val watcher = object : TextWatcher {
                override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                }
    
                override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                }
    
                override fun afterTextChanged(s: Editable?) {
                    s?.let {
                        trySend(s.toString())
                    }
                }
            }
            editText.addTextChangedListener(watcher)
            //在 flow 被 close 时调用,可以清理资源,一般必须要有
            awaitClose {
                editText.removeTextChangedListener(watcher)
            }
        }
    }
    

    根据输入框文本内容,我们去查询相关信息:

    fun testCallbackFlow(editText: EditText) {
        viewModelScope.launch {
            textChangeFlow(editText)
                .debounce(300)  //防抖处理,间隔 300ms 响应一次
                .flatMapLatest { keyword ->     //flatMapLatest 操作符,只处理最新的关键字,并且老的查询如果没有完成会自动 cancel 掉
                    println("keyword = $keyword")
                    flow { 
                        //根据输入关键字查询信息
                        var result = queryByKeyword(keyword)
                        emit(result)
                    }
                }.catch {
                    println("exception: $it")
                }
                .collect {
                    //查询到相关结果
                }
        }
    }
    

    如上代码所示,可以很优雅地实现我们想要的效果。

    相关文章

      网友评论

          本文标题:Android 利用 Kotlin callbackFlow优雅

          本文链接:https://www.haomeiwen.com/subject/wvatnrtx.html