美文网首页Android 开发收集的一些东西
知道handle的原理,但是你会使用吗

知道handle的原理,但是你会使用吗

作者: A_si | 来源:发表于2020-01-10 09:39 被阅读0次

    知道handle的原理,但是你会使用吗

    开发中常常遇到的需求,根据搜索框键入的关键字发起搜索,如果监听输入框的文字变化直接发起请求,会频繁调用接口,也会请求非用户想要的结果。比如天冷了,小明想买羽绒服,输入 ‘羽’,就会发起 ‘羽’ 关键字的请求,可能搜到羽毛球羽毛等无关内容,再次键入羽绒,会搜到羽绒服羽绒被羽绒棉。而小明只想要羽绒服,所以优雅的做法是监听输入完成才发起请求,但是每一次键入都会触发 TextWatcher 回调,为了实现监听完成,我们需要一个防抖函数。

    如果写过前端,对节流和防抖再熟悉不过了。网页的监听,输入的变化,都用得到。

    防抖函数

    在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

    js 的实现:

    var timer; // 维护同一个timer
    function debounce(fn, delay) {
        clearTimeout(timer);
        timer = setTimeout(function(){
            fn();
        }, delay);
    }
    

    那么 Android 中怎么理解和使用呢?小明买羽绒服的需求,在输入停顿的时间,发起请求,为了及时性,这个时间不易过长,假如500毫秒,那么就是在 TextWatcher 的 onTextChanged 回调后,500毫秒内不再输入,那么就发起请求。

    需求明白了,需要一个间隔时间,还有一个回调。然后如果在间隔时间内,就删除回调,重新计时,直到间隔时间内不再触发,说明输入完成,可以响应回调了。

    class Debounce(
        //间隔时间
        private val timeout: Long,
        //回调
        private val callback: () -> Unit
    
    ) {
        private val handler = Handler(Looper.getMainLooper())
        private val runnable = Runnable(callback)
    
        //触发防抖
        fun process() {
            handler.removeCallbacks(runnable)
            handler.postDelayed(runnable, timeout)
        }
    }
    

    因为 TextWatcher 的接口有三个,而我们只需要 onTextChanged ,所以写一个扩展函数:

    fun EditText.onChange(cb: (String) -> Unit) {
        this.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                cb(s.toString())
            }
    
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
        })
    }
    

    使用:

            val debounce = Debounce(500) {
                Log.e("MainActivity", "onCreate(MainActivity.java:13==${editText.text.toString()})")
            }
            editText.onChange { text ->
                debounce.process()
            }
    

    连续输入,并不会触发打印。这里要注意的是,不能直接使用handler.postDelayed(callback,timeout),这样就 removeCallbacks 就失效了。因为不是一个 Runnable了。

    如果想把变化后的 text 传进去,直接使用,我们可以换一种写法:

    const val MESSAGE_WHAT = 1
    
    class Debouncer(
        private val timeout: Long,
        private val callback: (String) -> Unit
    
    ) {
        private val handler = Handler(Looper.getMainLooper()) { message ->
            if (message.what != MESSAGE_WHAT) {
                return@Handler false
            }
            callback(message.obj as String)
            true
        }
    
        fun process(text: String) {
            handler.removeMessages(MESSAGE_WHAT)
            val message = handler.obtainMessage(MESSAGE_WHAT, text)
            handler.sendMessageDelayed(message, timeout)
        }
    }
    

    使用:

            val debouncer = Debouncer(500) { text ->
                Log.e("MainActivity", "onCreate(MainActivity.java:13==$text)")
            }
    
            editText.onChange { text ->
                debouncer.process(text)
            }
    
    

    节流函数

    每隔一段时间,只执行一次函数。

    节流函数我们遇到的最多,比如点击事件,防止连续点击,我们通常会去禁止多次点击。可以用 rxbind、计时、aop等,也可以用 handle 实现,这当然不是推荐的做法。

    class Throttler(
        private val timeout: Long,
        private val callback: () -> Unit
    ) {
        private val handler = Handler(Looper.getMainLooper())
        fun onAction() {
            if (handler.hasMessages(MESSAGE_WHAT)) {
                return
            }
            val message = handler.obtainMessage(MESSAGE_WHAT)
            handler.sendMessageDelayed(message, timeout)
            callback()
        }
    }
    

    使用:

            val throttler = Throttler(1000) {
                Log.e("MainActivity", "onCreate(MainActivity.java:22==${System.currentTimeMillis()})")
            }
            button.setOnClickListener { throttler.onAction() }
    

    如果在时间间隔内,消息还没有发送出去,先判断是否有消息,确定是否在时间间隔内。

    如果超过了间隔,再次设置间隔时间,并响应回调。

    相关文章

      网友评论

        本文标题:知道handle的原理,但是你会使用吗

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