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方法使用的时候进行初始化。
    // This field is initialized lazily in getSearchables(), and then never modified.    private Searchables mSearchables;
    private synchronized Searchables getSearchables() {        if (mSearchables == null) {            Log.i(TAG, "Building list of searchable activities");            new MyPackageMonitor().register(mContext, true);            mSearchables = new Searchables(mContext);            mSearchables.buildSearchableList();        }        return mSearchables;    }
mSearchables这个对象记录了系统中可以被搜索到的组件的信息,SearchManagerService的四个方法的实现都是来自于mSearchables这对象。通过代码注释可以看到,这个对象在延迟初始化之后就不会再修改,其实这个对象还是会修改的,在mSearchables初始化时,会向MyPackageMonitor进行注册,这个类的功能就是当有程序添加进系统或者被删除时,mSearchables会刷新它里面的信息。
    class MyPackageMonitor extends PackageMonitor {        @Override        public void onSomePackagesChanged() {            // Update list of searchable activities            getSearchables().buildSearchableList();            // Inform all listeners that the list of searchables has been updated.            Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);            mContext.sendBroadcast(intent);        }    }
mSearchables里面信息的填充,是调用了buildSearchableList()方法,下面我们深入到Searchables中,看看它是如何获取“被搜索”组件的信息的。 Searchables内有五个属性来保存“被搜索”组件信息的,它们都是在buildSearchableList()方法中被实例化的。
    private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;    private ArrayList<SearchableInfo> mSearchablesList = null;    private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;    private ComponentName mGlobalSearchActivity = null;    private ComponentName mWebSearchActivity = null;
在buildSearchableList方法中,我们可以看到,通过PackageManager的queryIntentActivities方法获取到了ACTION_SEARCH和ACTION_WEB_SEARCH的组件。
       final PackageManager pm = mContext.getPackageManager();        // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.        List<ResolveInfo> searchList;        final Intent intent = new Intent(Intent.ACTION_SEARCH);        searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);        List<ResolveInfo> webSearchInfoList;        final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);        webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
然后分别取遍历searchList和webSearchInfoList去填充mSearchablesMap对象、mSearchablesList对象 和mSearchablesInGlobalSearchList对象。
        if (searchList != null || webSearchInfoList != null) {            int search_count = (searchList == null ? 0 : searchList.size());            int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size());            int count = search_count + web_search_count;            for (int ii = 0; ii < count; ii++) {                // for each component, try to find metadata                ResolveInfo info = (ii < search_count)                        ? searchList.get(ii)                        : webSearchInfoList.get(ii - search_count);                ActivityInfo ai = info.activityInfo;                // Check first to avoid duplicate entries.                if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) {                    SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);                    if (searchable != null) {                        newSearchablesList.add(searchable);                        newSearchablesMap.put(searchable.getSearchActivity(), searchable);                        if (searchable.shouldIncludeInGlobalSearch()) {                            newSearchablesInGlobalSearchList.add(searchable);                        }                    }                }            }        }
那么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方法来获得。

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方法就可以在当前的显示组件上打开搜索框。我们看看这段代码都做了什么。
    public void startSearch(String initialQuery,                             boolean selectInitialQuery,                            ComponentName launchActivity,                            Bundle appSearchData,                            boolean globalSearch) {        if (globalSearch) {            startGlobalSearch(initialQuery, selectInitialQuery, appSearchData);            return;        }        ensureSearchDialog();        mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData);    }
在startSearch,它调用了startGlobalSearch方法开启了搜索服务;在ensureSearchDialog中初始化了mSearchDialog对象,设置了它的取消和消失事件;打开了搜索框。
    /* package */ void startGlobalSearch(String initialQuery, boolean selectInitialQuery,            Bundle appSearchData) {        ComponentName globalSearchActivity = getGlobalSearchActivity();        if (globalSearchActivity == null) {            Log.w(TAG, "No global search activity found.");            return;        }        Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        intent.setComponent(globalSearchActivity);        // Make sure that we have a Bundle to put source in        if (appSearchData == null) {            appSearchData = new Bundle();        } else {            appSearchData = new Bundle(appSearchData);        }        // Set source to package name of app that starts global search, if not set already.        if (!appSearchData.containsKey("source")) {            appSearchData.putString("source", mContext.getPackageName());        }        intent.putExtra(APP_DATA, appSearchData);        if (!TextUtils.isEmpty(initialQuery)) {            intent.putExtra(QUERY, initialQuery);        }        if (selectInitialQuery) {            intent.putExtra(EXTRA_SELECT_QUERY, selectInitialQuery);        }        try {            if (DBG) Log.d(TAG, "Starting global search: " + intent.toUri(0));            mContext.startActivity(intent);        } catch (ActivityNotFoundException ex) {            Log.e(TAG, "Global search activity not found: " + globalSearchActivity);        }    }
可以看到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

更多相关文章

  1. Android中调用Rest web服务
  2. 打不死的小强--双进程守护
  3. Android(安卓)屏幕的旋转 onConfigurationChanged方法
  4. Android的System Server
  5. ArcGIS for Android
  6. android中getCacheDir(),getFilesDir(),getExternalFilesDir(),getE
  7. android 当环境配置发生变化 例如语言 屏幕变化
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)TabLayout添加自定义分割线
  2. Android之Android的数据存储--SD卡
  3. [006] 百度地图移动版API终于发布了(Andr
  4. Android(安卓)SimpleAdapter显示ListView
  5. 五年之内,Android 或将被取代?
  6. android中检测网络连接状态简单总结
  7. Android(安卓)rom开发:app运行异常,报libc:
  8. 一、Android体系及系统架构
  9. 编译XT720 gingerbread
  10. android中BuildConfig.DEBUG的使用