首页 > 代码库 > android里Filter的研究

android里Filter的研究

一、概述:
     过滤器通过过滤模式来约束数据,过滤操作是通过调用filter(CharSequence)或者filter(CharSequence, android.widget.Filter.FilterListener)这些异步方法来完成的。以上方法一旦被调用,过滤请求就会被递交到请求队列中等待处理,同时该操作会取消那些之前递交的但是还没有被处理的请求。
     其工作过程可以简单理解为:有一个工作队列在处理任务,但每次只能处理一个任务。每次提交一个任务的时候,如果工作队列中没有正在执行的任务,则立马执行该任务。如果有执行的任务,则该任务等待。如果队列中有正在执行的任务,并且有等待的任务,则取消所有等待的任务,把该任务放入等待的过程中。所以在任何时候,最多只能有一个任务在执行,一个任务在等待。

二、主要方法:   
     public void final filter(CharSequence constraint, FilterListener listener);
     执行一个异步的过滤操作请求。

     protected abstract FilterResults performFiltering(CharSequence constraint);
     根据约束条件调用一个工作线程来过滤数据,子类必须实现该方法来执行过滤操作。过滤结果以Filter.FilterResults的形式返回,然后在UI线程中通过publishResults(CharSequence, android.widget.Filter.FilterResults)方法来发布。      

     protected abstract void publishResults(CharSequence constraint, FilterResults results);
    通过调用UI线程在用户界面发布过滤结果。

三、实现原理:
     在一个新开的工作线程里执行过滤的操作,过滤的结果在UI线程里publish。

     1.在Filter类里使用了2个Handler:RequestHandler是用来处理过滤操作的,ResultHandler是用来发布过滤结果的。
            private Handler mThreadHandler;
            private Handler mResultHandler;    
            
            public Filter() {
                 mResultHandler = new ResultHandler();
            }
            可见,ResultHandler在构造方法里创建,一般说来是与UI线程进行绑定的,最后过滤出结果后,publishResults是通过ResultHandler来进行分发消息的,所以最后的回调过程是在UI线程执行的,这里实现的很巧妙,可以学习之。
            疑问:如果Filter类是在一个子线程里进行创建的,则在publishResults时,就不是在UI线程里执行了?实际结果是,如果在子线程里 new一个Filter对象,则会直接报错,原因是子线程里还没有构造Looper对象,需要在new Handler()之前调用Looper.prepare()方法。
     
     2.filter请求流程。
          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);
             }
          }     
          可以看到,在这里才会创建一个HandlerThread对象,这是一个子线程,随后会创建一个RequestHandler,该Handler会与该子线程绑定。以后所有的过滤请求,都通过RequestHandler进行分发,所有的过滤操作,都会在子线程中执行。
          在发起本次请求之前,会通过RequestHandler取消掉所有还未执行的消息。
     
          再看一下RequestHandler的内部实现,
          
               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();
                    }
                    //在这里可以看到,会调用performFiltering()方法进行过滤,接着会通过ResultHandler进行结果发布。

                    synchronized (mLock) {
                        if (mThreadHandler != null) {
                            Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN);
                            mThreadHandler.sendMessageDelayed(finishMessage, 3000);
                        }
                    }
                    //在所有操作执行完毕后,会延迟3秒发送一个结束的消息,该消息会终止掉HandlerThread线程里的消息循环。这个地方很巧妙:1.如果连续的发起过滤请求,则会一直使用之前创建的HandlerThread线程来处理过滤操作,不会频繁的创建线程,过多的损耗性能;2.如果长时间没有过滤请求,比如超过3s后,则会终止点该线程,因为该线程实际上是个空闲的线程,可以终止掉,同样的减少性能损耗。注意要加锁操作。

                    break;
                case FINISH_TOKEN:           //这里处理终止线程的操作
                    synchronized (mLock) {
                        if (mThreadHandler != null) {
                            mThreadHandler.getLooper().quit();
                            mThreadHandler = null;
                        }
                    }
                    break;

四、总结:
     1.巧妙的使用了HandlerThread来创建一个带Looper的Thread。
     2.通过与不同线程绑定的Handler,来实现异步的过滤查询操作。
     3.类似AutoCompleteTextView的实现,通过Filter来实现。
     4.参考Filter的实现方式,可以在需要频繁异步查询的地方,采用同样的设计方法。比如输入框里的联网自动提示功能。
     5.AsyncQueryHandler实现原理与之类似,都是采用了HandlerThread,使用一个工作线程来处理事情。


android里Filter的研究