[zz] SearchManager分析

来源:互联网 发布:java thread getname 编辑:程序博客网 时间:2024/05/16 06:36

ref: http://willsunforjava.iteye.com/blog/1674817


1. 前言

Android系统提供了的一种搜索服务,利用此服务可以实现对系统中的应用、联系人、SMS等进行搜索,也提供转入浏览器中的搜索。Android Develop Blog中有一篇文章赞美了Android搜索功能的强大快捷——《Introducing Quick Search Box for Android》。

SearchManager是搜索服务的入口,可以通过context.getSystemService(Context.SEARCH_SERVICE)获取SearchManager对象。SearchManager像其他ActivityManager、PackageManager等服务一样,是随系统启动一起启动的服务,并且启动后向ServiceManager注册自己,客户端最终获取搜索服务的途径也是通过binder机制向ServiceManager获取的。

从搜索的角度来看,应用可分为三类: unsearchable 类型应用、Query-Search 类型应用和 Filter-Search 类型应用。大部分应用是属于后两种。不过,即便是第一种类型,应用也仍旧支持对搜索的调用。后两种的区分就在于,Query-Search 类型应用执行 batch-mode 搜索,每一个查询字符串都被转化成结果列表;Filter-Search 类型应用则提供 filter-as-you-type 搜索。通常来讲,对基于网络的数据进行 Query Search,而对本地数据,则需要 Filter Search。

 

2. 整体类图

 


从上面的类图可以看出SearchManager提供的搜索服务最终是由ISearchManager这个接口的实现类提供的。同时SearchManager又实现了Dialog相关的两个接口OnDismissListener和OnCancelListener,以及内置一个SearchDialog,这些是为了搜索框实现服务的,因为在Android系统中的搜索服务界面就是一个Quick Search Box。下面我们就来先分析一个ISearchManager这一块提供了什么样的搜索服务以及如何提供搜索服务。

 

3.  SearchManagerService 提供搜索服务

ISearchManager是利用AIDL定义的,因此它的代码利用aapt编译生成的,组织方式使用了代理模式。ISearchManager.Stub的实现类是SearchManagerService,所以真正提供搜索服务的是这个类。

ISearchManager接口中定义搜索相关的基础服务,有如下的方法:

  • public SearchableInfo getSearchableInfo(ComponentName launchActivity) throws RemoteException;
  • public List<SearchableInfo> getSearchablesInGlobalSearch() throws RemoteException;
  • public ComponentName getGlobalSearchActivity() throws RemoteException;
  • public ComponentName getWebSearchActivity() throws RemoteException;
这些方法的实现都在SearchManagerService中,我们到这个类中看看这四个方法都提供什么样的功能。
SearchManagerService类中有个Searchables类型的属性mSearchables,它会在getSearchables方法使用的时候进行初始化。
Java代码  收藏代码
  1. // This field is initialized lazily in getSearchables(), and then never modified.  
  2. private Searchables mSearchables;  
Java代码  收藏代码
  1. private synchronized Searchables getSearchables() {  
  2.     if (mSearchables == null) {  
  3.         Log.i(TAG, "Building list of searchable activities");  
  4.         new MyPackageMonitor().register(mContext, true);  
  5.         mSearchables = new Searchables(mContext);  
  6.         mSearchables.buildSearchableList();  
  7.     }  
  8.     return mSearchables;  
  9. }  
 mSearchables这个对象记录了系统中可以被搜索到的组件的信息,SearchManagerService的四个方法的实现都是来自于mSearchables这对象。通过代码注释可以看到,这个对象在延迟初始化之后就不会再修改,其实这个对象还是会修改的,在mSearchables初始化时,会向MyPackageMonitor进行注册,这个类的功能就是当有程序添加进系统或者被删除时,mSearchables会刷新它里面的信息。
Java代码  收藏代码
  1. class MyPackageMonitor extends PackageMonitor {  
  2.     @Override  
  3.     public void onSomePackagesChanged() {  
  4.         // Update list of searchable activities  
  5.         getSearchables().buildSearchableList();  
  6.         // Inform all listeners that the list of searchables has been updated.  
  7.         Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);  
  8.         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  9.         mContext.sendBroadcast(intent);  
  10.     }  
  11. }  
 mSearchables里面信息的填充,是调用了buildSearchableList()方法,下面我们深入到Searchables中,看看它是如何获取“被搜索”组件的信息的。
Searchables内有五个属性来保存“被搜索”组件信息的,它们都是在buildSearchableList()方法中被实例化的。
Java代码  收藏代码
  1. private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;  
  2. private ArrayList<SearchableInfo> mSearchablesList = null;  
  3. private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;  
  4. private ComponentName mGlobalSearchActivity = null;  
  5. private ComponentName mWebSearchActivity = null;  
在buildSearchableList方法中,我们可以看到,通过PackageManager的queryIntentActivities方法获取到了ACTION_SEARCH和ACTION_WEB_SEARCH的组件。
Java代码  收藏代码
  1. final PackageManager pm = mContext.getPackageManager();  
  2.   
  3.  // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.  
  4.  List<ResolveInfo> searchList;  
  5.  final Intent intent = new Intent(Intent.ACTION_SEARCH);  
  6.  searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);  
  7.   
  8.  List<ResolveInfo> webSearchInfoList;  
  9.  final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);  
  10.  webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);  
 然后分别取遍历searchList和webSearchInfoList去填充mSearchablesMap对象、mSearchablesList对象
和 mSearchablesInGlobalSearchList对象。
Java代码  收藏代码
  1. if (searchList != null || webSearchInfoList != null) {  
  2.     int search_count = (searchList == null ? 0 : searchList.size());  
  3.     int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size());  
  4.     int count = search_count + web_search_count;  
  5.     for (int ii = 0; ii < count; ii++) {  
  6.         // for each component, try to find metadata  
  7.         ResolveInfo info = (ii < search_count)  
  8.                 ? searchList.get(ii)  
  9.                 : webSearchInfoList.get(ii - search_count);  
  10.         ActivityInfo ai = info.activityInfo;  
  11.         // Check first to avoid duplicate entries.  
  12.         if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) {  
  13.             SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);  
  14.             if (searchable != null) {  
  15.                 newSearchablesList.add(searchable);  
  16.                 newSearchablesMap.put(searchable.getSearchActivity(), searchable);  
  17.                 if (searchable.shouldIncludeInGlobalSearch()) {  
  18.                     newSearchablesInGlobalSearchList.add(searchable);  
  19.                 }  
  20.             }  
  21.         }  
  22.     }  
  23. }  
 那么Searchables五个属性中到底在系统启动的时候存了那些信息呢,我们可以通过在编写一个Android小应用,利用反射查看一些这些信息是什么。通过测试,获得这些属性的包含的值。
mSearchablesList 中存储的是系统中可以"被搜索"到的应用程序的主Activity,当向系统中添加应用时,如果应用在AndroidManifest.xml中被表明是可以被搜索到的,在mSearchablesList 就会有此应用。
mSearchablesMap是mSearchablesList 的Map形式,便于搜索查询相关应用的主Activity。
mGlobalSearchActivity 对应的类是:com.android.quicksearchbox.SearchActivity。
mWebSearchActivity 对应的类是:com.android.quicksearchbox.google.GoogleSearch。
mSearchablesInGlobalSearchList 中存储的对象类型有如下:
com.android.providers.applications.ApplicationLauncher
com.android.mms.ui.SearchActivity
com.android.contacts.SearchResultsActivity
com.android.browser.BookmarkSearch
com.android.music.QueryBrowserActivity
可以看出系统初始的能够被全局搜索的应用有短信、联系人、音乐、浏览器书签和应用程序管理。
 

4. SearchManager提供搜索相关操作

SearchManager是搜索服务的入口,客户端获取SearchManager对象可以调用getSystemService方法来获得。

 

Java代码  收藏代码
  1. SearchManager searchManager = (SearchManager)this.getSystemService(SEARCH_SERVICE);  

可以看到SearchManager内部定义了两个回调接口OnDismissListener和OnCancelListener,并且实现了DialogInterface.OnDismissListener和DialogInterface.OnCancelListener接口,客户端可以通过setOnDismissListener和setOnCancelListener来设置搜索框消失和取消时的事件处理。

SearchManager有个SearchDialog类型的对象mSearchDialog,它就是搜索时显示的搜索框。

看一下SearchManager中的一些重要方法,

 

 

  • public void startSearch(String initialQuery, boolean selectInitialQuery, ComponentName launchActivity, Bundle appSearchData, boolean globalSearch) 
客户端可以通过调用startSearch方法就可以在当前的显示组件上打开搜索框。我们看看这段代码都做了什么。
Java代码  收藏代码
  1. public void startSearch(String initialQuery,   
  2.                         boolean selectInitialQuery,  
  3.                         ComponentName launchActivity,  
  4.                         Bundle appSearchData,  
  5.                         boolean globalSearch) {  
  6.     if (globalSearch) {  
  7.         startGlobalSearch(initialQuery, selectInitialQuery, appSearchData);  
  8.         return;  
  9.     }  
  10.   
  11.     ensureSearchDialog();  
  12.   
  13.     mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData);  
  14. }  
 在startSearch,它调用了startGlobalSearch方法开启了搜索服务;在ensureSearchDialog中初始化了mSearchDialog对象,设置了它的取消和消失事件;打开了搜索框。
Java代码  收藏代码
  1. /* package */ void startGlobalSearch(String initialQuery, boolean selectInitialQuery,  
  2.         Bundle appSearchData) {  
  3.     ComponentName globalSearchActivity = getGlobalSearchActivity();  
  4.     if (globalSearchActivity == null) {  
  5.         Log.w(TAG, "No global search activity found.");  
  6.         return;  
  7.     }  
  8.     Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);  
  9.     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  10.     intent.setComponent(globalSearchActivity);  
  11.     // Make sure that we have a Bundle to put source in  
  12.     if (appSearchData == null) {  
  13.         appSearchData = new Bundle();  
  14.     } else {  
  15.         appSearchData = new Bundle(appSearchData);  
  16.     }  
  17.     // Set source to package name of app that starts global search, if not set already.  
  18.     if (!appSearchData.containsKey("source")) {  
  19.         appSearchData.putString("source", mContext.getPackageName());  
  20.     }  
  21.     intent.putExtra(APP_DATA, appSearchData);  
  22.     if (!TextUtils.isEmpty(initialQuery)) {  
  23.         intent.putExtra(QUERY, initialQuery);  
  24.     }  
  25.     if (selectInitialQuery) {  
  26.         intent.putExtra(EXTRA_SELECT_QUERY, selectInitialQuery);  
  27.     }  
  28.     try {  
  29.         if (DBG) Log.d(TAG, "Starting global search: " + intent.toUri(0));  
  30.         mContext.startActivity(intent);  
  31.     } catch (ActivityNotFoundException ex) {  
  32.         Log.e(TAG, "Global search activity not found: " + globalSearchActivity);  
  33.     }  
  34. }  
 可以看到startGlobalSearch做的事情就是打开globalSearchActivity,上面我分析可以知道globalSearchActivity就是com.android.quicksearchbox.SearchActivity,其实就是打开了QuickSearchBox应用的首界面。
  • public void triggerSearch(String query,  ComponentName launchActivity, Bundle appSearchData) 
triggerSearch与startSearch方法类似,只是selectInitialQuery与globalSearch设置为false
  • public void stopSearch() 
相当于触发了SearchManager的cancel事件,将dialog取消掉了
  • public Cursor getSuggestions(SearchableInfo searchable, String query, int limit)

这个方法被标为@hide的,是android内部的api。

 

5.  SuggestionsAdapter填充SearchDialog的下拉框数据

 

6.  SearchRecentSuggestions与SearchRecentSuggestionsProvider辅助实现搜索的历史记录

 

7.  搜索在系统组件中的分布

Activity中有方法onSearchRequested

 

8.  meta-data标签的使用

参考:http://blog.csdn.net/happy_6678/article/details/6556771

 

9.  与ContentProvider相关

参考:http://www.cnblogs.com/over140/archive/2011/12/28/2304393.html

0 0
原创粉丝点击