美文网首页
Filter过滤器源码解析与使用

Filter过滤器源码解析与使用

作者: Magic旭 | 来源:发表于2019-06-09 14:54 被阅读0次

Filter类使用介绍

Filter类如何使用

Filter是一个抽象的类,在使用方面上你可以选择匿名类声明,也可以选择继承抽象类。

//匿名类
private Filter filter = new Filter() {}

//继承抽象类
class MyFilter extends Filter{
……
}
Filter的两个抽象方法
  • protected FilterResults performFiltering(CharSequence constraint)
    performFiltering 方法是Filter类封装在子线程中运行的方法,就意味着我们可以在这个方法体里面做网络请求等相关操作。

  • protected void publishResults(CharSequence constraint, FilterResults results)
    publishResults 方法是Filter类封装在主线程运行的方法,其中返回参数results是由performFiltering方法返回已经处理好的数据。
    注:其中两个方法如何进行信息传递的,后面我深入源码讲解,请耐心看下去。

Filterable接口

Filterable接口作用

实现Filterable接口是为了方便返回我们实例化的Filter类型,让我可以直接调用filter执行数据过滤操作。

//我这里是在adapter里面实现接口和实例化Filter类型的。
adapter.getFilter().filter("我");
数据处理逻辑

其中数据处理我都写在了Adapter里面,FilterApiParser是模拟一个网络请求操作。

//我的Adapter里面执行的方法如下,因为我比较懒,直接是用匿名子类来声明变量了。
private Filter filter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults filterResults = new FilterResults();
            ArrayList<FilterBean.DataBean.ItemsBean> result = new ArrayList<>();
            //网络请求操作
            FilterBean.DataBean filterBean = new FilterApiParser(context,"filter.json").getResponseFromLocal();
           for(FilterBean.DataBean.ItemsBean i : filterBean.items){
                MyLog.i("origin item"+i.toString());
                if(i.title.contains(constraint)){
                    result.add(i);
                }
            }
            filterResults.values = result;
            filterResults.count = result.size();
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            ArrayList<FilterBean.DataBean.ItemsBean> result =  (ArrayList<FilterBean.DataBean.ItemsBean>)results.values;
            for(FilterBean.DataBean.ItemsBean item: result){
                MyLog.i("item"+item.toString());
            }
        }
    };

//Filterable接口返回上述的filter变量
@Override
    public Filter getFilter() {
        return filter;
    }

运行结果

在子线程中请求网络,过滤数据后存在一个FilterResults类里返回,然后到publishResults打印筛选后的结果。


image.png

Filter源码解析

filter方法的源码
  1. 实例化HandlerThread,通过HandlerThread里面的Looper创建属于子线程通信的Handler(RequestHandler)。HandlerThread源码可以看我上一篇的源码解析。
  2. 通过obtainMessage方法,从消息池中取出对应的Message,清楚Handler消息队列里面的message,重新发送新的mesage事件。
  3. why?为什么要重新创建Handler呢?HandlerThread里面不是有getThreadHandler方法吗?不是能拿到对应的Handler吗?但是你详细一看,是@hide修饰的,google官方把api隐藏起来了。所以如果我们要创建子线程的通信,就必须继承Handler,使用HandlerThread的looper初始化我们的Handler实现子线程通信。
public final void filter(CharSequence constraint, FilterListener listener) {
        synchronized (mLock) {
            if (mThreadHandler == null) {
                HandlerThread thread = new HandlerThread(
                        THREAD_NAME, android.os.Process.THREAD_PRIORITY_BACKGROUND);
                thread.start();
                mThreadHandler = new RequestHandler(thread.getLooper());
            }

            final long delay = (mDelayer == null) ? 0 : mDelayer.getPostingDelay(constraint);
            
            Message message = mThreadHandler.obtainMessage(FILTER_TOKEN);
    
            RequestArguments args = new RequestArguments();
            // make sure we use an immutable copy of the constraint, so that
            // it doesn't change while the filter operation is in progress
            args.constraint = constraint != null ? constraint.toString() : null;
            args.listener = listener;
            message.obj = args;
    
            mThreadHandler.removeMessages(FILTER_TOKEN);
            mThreadHandler.removeMessages(FINISH_TOKEN);
            mThreadHandler.sendMessageDelayed(message, delay);
        }
    }
RequestHandler 与 ResultHandler
  1. 因为该Handler的通过子线程的looper创建的,所以它属于子线程的Handler。
  2. 可以看到handleMessage(Message msg)方法中接收到 FILTER_TOKEN 消息类型时,取出消息内容,执行performFiltering(CharSequence constraint)方法,这个方法就是上面说继承Filter类必须实现的抽象方法之一,它的为什么是运行在子线程中,这里的源码已经能明确告知你了。
  3. 可以看到这里的mResultHandler在finally语句块最后执行了sendToTarget()方法,这里是数据处理结束后,通知回主线程。可以在ResultsHandler类的handleMessage(Message msg)方法中执行了抽象方法publishResults(CharSequence constraint,FilterResults results),这个方法就是上面说继承Filter类必须实现的抽象方法之一,它的为什么是运行在主线程中呢?请看第4点。
  4. ResultHandler类是在Filter构造方法中实例化的,并没有传任何构造函数。Handler的无参构造函数,里面内部实现是获取 mLooper = Looper.myLooper()方法获取构建Handler必备的Looper参数,而这里获取的恰好是UI线程的Looper,所以说publishResults方法是在UI线程上运行的。如果你的Filter是在子线程上初始化的,那么Handler内部的Looper将会对应该子线程。
  5. 最后所有操作都执行结束后,通过mResultHandler延迟3秒发送FINISH_TOKEN消息事件,来表示所以操作已经结束了。mThreadHandler.getLooper().quit()停止消息队列使用,已经无法再添加新的消息入队列中去了。至此整一套Filter过滤器的使用就已经全部结束了。
private class RequestHandler extends Handler {
        public RequestHandler(Looper looper) {
            super(looper);
        }
        
        /**
         * <p>Handles filtering requests by calling
         * {@link Filter#performFiltering} and then sending a message
         * with the results to the results handler.</p>
         *
         * @param msg the filtering request
         */
        public void handleMessage(Message msg) {
            int what = msg.what;
            Message message;
            switch (what) {
                case FILTER_TOKEN:
                    RequestArguments args = (RequestArguments) msg.obj;
                    try {
                        args.results = performFiltering(args.constraint);
                    } catch (Exception e) {
                        args.results = new FilterResults();
                        Log.w(LOG_TAG, "An exception occured during performFiltering()!", e);
                    } finally {
                        message = mResultHandler.obtainMessage(what);
                        message.obj = args;
                        message.sendToTarget();
                    }

                    synchronized (mLock) {
                        if (mThreadHandler != null) {
                            Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN);
                            mThreadHandler.sendMessageDelayed(finishMessage, 3000);
                        }
                    }
                    break;
                case FINISH_TOKEN:
                    synchronized (mLock) {
                        if (mThreadHandler != null) {
                            mThreadHandler.getLooper().quit();
                            mThreadHandler = null;
                        }
                    }
                    break;
            }
        }
    }


private class ResultsHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            RequestArguments args = (RequestArguments) msg.obj;

            publishResults(args.constraint, args.results);
            if (args.listener != null) {
                int count = args.results != null ? args.results.count : -1;
                args.listener.onFilterComplete(count);
            }
        }
    }

总结

  1. 其实当初学习Android开发的时候,觉得看源码很枯燥无味,都不是很想看。但是现在慢慢积累到一定相关知识后,再去看源码,你会发现看源码能学习到更多别人编程的思维,你会发现这样子会有助于往后提升你自己的代码水平。我比较鼓励大家先从简单的源码入手,一步一步看,加油。
  2. Filter其中用于搜索功能的实现上比较方法,更多用于Adapter中请求与过滤数据,而且不需要创建子线程,不需要创建子线程与主线程之间的通信桥梁。挺方便的,如果大家遇上搜索相关功能鼓励大家使用下。

相关文章

网友评论

      本文标题:Filter过滤器源码解析与使用

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