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打印筛选后的结果。

Filter源码解析
filter方法的源码
- 实例化HandlerThread,通过HandlerThread里面的Looper创建属于子线程通信的Handler(RequestHandler)。HandlerThread源码可以看我上一篇的源码解析。
- 通过obtainMessage方法,从消息池中取出对应的Message,清楚Handler消息队列里面的message,重新发送新的mesage事件。
- 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
- 因为该Handler的通过子线程的looper创建的,所以它属于子线程的Handler。
- 可以看到handleMessage(Message msg)方法中接收到 FILTER_TOKEN 消息类型时,取出消息内容,执行performFiltering(CharSequence constraint)方法,这个方法就是上面说继承Filter类必须实现的抽象方法之一,它的为什么是运行在子线程中,这里的源码已经能明确告知你了。
- 可以看到这里的mResultHandler在finally语句块最后执行了sendToTarget()方法,这里是数据处理结束后,通知回主线程。可以在ResultsHandler类的handleMessage(Message msg)方法中执行了抽象方法publishResults(CharSequence constraint,FilterResults results),这个方法就是上面说继承Filter类必须实现的抽象方法之一,它的为什么是运行在主线程中呢?请看第4点。
- ResultHandler类是在Filter构造方法中实例化的,并没有传任何构造函数。Handler的无参构造函数,里面内部实现是获取 mLooper = Looper.myLooper()方法获取构建Handler必备的Looper参数,而这里获取的恰好是UI线程的Looper,所以说publishResults方法是在UI线程上运行的。如果你的Filter是在子线程上初始化的,那么Handler内部的Looper将会对应该子线程。
- 最后所有操作都执行结束后,通过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);
}
}
}
总结
- 其实当初学习Android开发的时候,觉得看源码很枯燥无味,都不是很想看。但是现在慢慢积累到一定相关知识后,再去看源码,你会发现看源码能学习到更多别人编程的思维,你会发现这样子会有助于往后提升你自己的代码水平。我比较鼓励大家先从简单的源码入手,一步一步看,加油。
- Filter其中用于搜索功能的实现上比较方法,更多用于Adapter中请求与过滤数据,而且不需要创建子线程,不需要创建子线程与主线程之间的通信桥梁。挺方便的,如果大家遇上搜索相关功能鼓励大家使用下。
网友评论