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. 整体类图


Android内核学习——SearchManager分析_第1张图片

从上面的类图可以看出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 屏幕的旋转 onConfigurationChanged方法
  2. 查看android里的数据库的内容的方法
  3. 让android定时关机的实现方法
  4. android Listview中button 和Listview可同时点击的方法
  5. android添加后门的一种方法
  6. Android ViewGroup中有关测量的方法

随机推荐

  1. 避免 Android(安卓)Studio 编译 APK 出现
  2. Android(安卓)Studio从零开始安装
  3. Android’s HTTP Clients--Android(安卓)
  4. Android下Opengl程序显示文字的简单方法
  5. Android安全分析挑战:运行时篡改Dalvik字
  6. 实现Android多张图片选择
  7. Android(安卓)Canvas绘图详解【转】
  8. 使用SmsManager服务群发短信
  9. android中get方式和post方式提交数据到服
  10. Android代码内存优化建议-Android官方篇