SearchManager使用以及源码解析

使用:

客户端获取SearchManager对象,拿到对象之后就可以获取支持全局搜索的activity数据源list。

//获取SearchManager对象
mSearchManager = (SearchManager) this.getApplicationContext().getSystemService(Context.SEARCH_SERVICE);
//获取支持全局搜索的activity数据源list
List<SearchableInfo> infoList = mSearchManager.getSearchablesInGlobalSearch();
//获取数据源中配置的searchable.xml中提供的信息
for (SearchableInfo info : infoList) {
            String suggestPackage = info.getSuggestPackage();//包名
            String suggestAuthority = info.getSuggestAuthority();//其实就是定义contentProvider时候的authority
            String suggestIntentAction = info.getSuggestIntentAction();//支持的intentAction
            String suggestIntentData = info.getSuggestIntentData();//额外的数据
            String suggestPath = info.getSuggestPath();//能查询的路径
            String suggestSelection = info.getSuggestSelection();//检索的条件,也就是Sql语句where后面的条件
            ComponentName searchActivity = info.getSearchActivity();//被查询的activity
            Log.i(TAG, "suggestAuthority =  "+  suggestAuthority);
            Log.i(TAG, "suggestPackage =  "+  suggestPackage);
            Log.i(TAG, "suggestIntentAction =  " + suggestIntentAction);
            Log.i(TAG, "suggestIntentData =  " + suggestIntentData);
            if (!Objects.equals(suggestPackage, "com.android.providers.contacts"))
                querySearchInfo(suggestIntentData, string);
        }

如何配置应用成为搜索数据源在之前的文章中有详细说明

android13配置GlobalSearch全局搜索_LingoA的博客-CSDN博客

源码解析:

路径frameworks/base/services/core/java/com/android/server/search

SearchManagerService提供了多个接口获取不同的搜索源,包括获取支持web搜索的activity,获取所有支持搜索的activity等。。。

@Override
    public List<SearchableInfo> getSearchablesInGlobalSearch() {
        return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
    }

    @Override
    public List<ResolveInfo> getGlobalSearchActivities() {
        return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
    }

    /**
     * Gets the name of the global search activity.
     */
    @Override
    public ComponentName getGlobalSearchActivity() {
        return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
    }

    /**
     * Gets the name of the web search activity.
     */
    @Override
    public ComponentName getWebSearchActivity() {
        return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
    }

之后会调用getSearchables(int userId)

 
private Searchables getSearchables(int userId, boolean forceUpdate) {
        final long token = Binder.clearCallingIdentity();
        try {
            final UserManager um = mContext.getSystemService(UserManager.class);
            if (um.getUserInfo(userId) == null) {
                throw new IllegalStateException("User " + userId + " doesn't exist");
            }
            if (!um.isUserUnlockingOrUnlocked(userId)) {
                throw new IllegalStateException("User " + userId + " isn't unlocked");
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        synchronized (mSearchables) {
            Searchables searchables = mSearchables.get(userId);
            if (searchables == null) {
                searchables = new Searchables(mContext, userId);
                searchables.updateSearchableList();
                mSearchables.append(userId, searchables);
            } else if (forceUpdate) {
                searchables.updateSearchableList();
            }
            return searchables;
        }
    }
getSearchables(int userId, boolean forceUpdate)内部会通过userId判断当前用户是否获取过searchables,如果没有则创建新的searchables对象并调用updateSearchableList()进行数据的初始化操作之后会存入SparseArray<Searchables> mSearchables中。
接下来看updateSearchableList()的内部代码
//Searchables.java
 public void updateSearchableList() {
        // These will become the new values at the end of the method
        HashMap<ComponentName, SearchableInfo> newSearchablesMap
                                = new HashMap<ComponentName, SearchableInfo>();
        ArrayList<SearchableInfo> newSearchablesList
                                = new ArrayList<SearchableInfo>();
        ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                = new ArrayList<SearchableInfo>();

        // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
        List<ResolveInfo> searchList;
        final Intent intent = new Intent(Intent.ACTION_SEARCH);

        final long ident = Binder.clearCallingIdentity();
        try {
            searchList = queryIntentActivities(intent,
                    PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);

            List<ResolveInfo> webSearchInfoList;
            final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
            webSearchInfoList = queryIntentActivities(webSearchIntent,
                    PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);

            // analyze each one, generate a Searchables record, and record
            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,
                                mUserId);
                        if (searchable != null) {
                            newSearchablesList.add(searchable);
                            newSearchablesMap.put(searchable.getSearchActivity(), searchable);
                            if (searchable.shouldIncludeInGlobalSearch()) {
                                newSearchablesInGlobalSearchList.add(searchable);
                            }
                        }
                    }
                }
            }

            List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities();

            // Find the global search activity
            ComponentName newGlobalSearchActivity = findGlobalSearchActivity(
                    newGlobalSearchActivities);

            // Find the web search activity
            ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity);

            // Store a consistent set of new values
            synchronized (this) {
                mSearchablesMap = newSearchablesMap;
                mSearchablesList = newSearchablesList;
                mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
                mGlobalSearchActivities = newGlobalSearchActivities;
                mCurrentGlobalSearchActivity = newGlobalSearchActivity;
                mWebSearchActivity = newWebSearchActivity;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
updateSearchableList内部会调用queryIntentActivities()查找出添加
"android.intent.action.SEARCH"或"android.intent.action.WEB_SEARCH"这两个action的activity中是否并分别保存在list中,

之后再获取activityInfo,并调用getActivityMetaData(mContext, ai, mUserId)解析activity并生成searchable对象并存入list

现在看queryIntentActivities():

//Searchables.java
private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
        List<ResolveInfo> activities = null;
        try {
            activities =
                    mPm.queryIntentActivities(intent,
                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                    flags | PackageManager.MATCH_INSTANT, mUserId).getList();
        } catch (RemoteException re) {
            // Local call
        }
        return activities;
    }

内部会调用packageManager的queryIntentActivities去查找包含对应intent的activity(系统启动完成后会发出BOOT_COMPLETE广播,PMS收到广播后会遍历并保存所有activity信息,这块代码有空再看...)

再看getActivityMetaData():

//SearchableInfo.java
//Gets search information for the given activity.
//Params:
//context – Context to use for reading activity resources. activityInfo – Activity //to get search information from.
//Returns:
//Search information about the given activity, or null if the activity has no or //invalid searchability meta-data.
//hide
//For use by SearchManagerService

public static SearchableInfo getActivityMetaData(Context context, ActivityInfo activityInfo,
            int userId) {
        Context userContext = null;
        try {
            userContext = context.createPackageContextAsUser("system", 0,
                new UserHandle(userId));
        } catch (NameNotFoundException nnfe) {
            Log.e(LOG_TAG, "Couldn't create package context for user " + userId);
            return null;
        }
        // for each component, try to find metadata
        XmlResourceParser xml = 
                activityInfo.loadXmlMetaData(userContext.getPackageManager(), MD_LABEL_SEARCHABLE);
        if (xml == null) {
            return null;
        }
        ComponentName cName = new ComponentName(activityInfo.packageName, activityInfo.name);
        
        SearchableInfo searchable = getActivityMetaData(userContext, xml, cName);
        xml.close();
        
......
.....
        return searchable;
    }

//Get the metadata for a given activity
//Params:
//context – runtime context xml – XML parser for reading attributes cName – The //component name of the searchable activity
//result
//A completely constructed SearchableInfo, or null if insufficient XML data for i

private static SearchableInfo getActivityMetaData(Context context, XmlPullParser xml,
            final ComponentName cName)  {
        SearchableInfo result = null;
        Context activityContext = createActivityContext(context, cName);
        if (activityContext == null) return null;

        // in order to use the attributes mechanism, we have to walk the parser
        // forward through the file until it's reading the tag of interest.
        try {
            int tagType = xml.next();
            while (tagType != XmlPullParser.END_DOCUMENT) {
                if (tagType == XmlPullParser.START_TAG) {
                    if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE)) {
                        AttributeSet attr = Xml.asAttributeSet(xml);
                        if (attr != null) {
                            try {
                                result = new SearchableInfo(activityContext, attr, cName);
                            } catch (IllegalArgumentException ex) {
                                Log.w(LOG_TAG, "Invalid searchable metadata for " +
                                        cName.flattenToShortString() + ": " + ex.getMessage());
                                return null;
                            }
                        }
                    } else if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY)) {
                        if (result == null) {
                            // Can't process an embedded element if we haven't seen the enclosing
                            return null;
                        }
                        AttributeSet attr = Xml.asAttributeSet(xml);
                        if (attr != null) {
                            try {
                                result.addActionKey(new ActionKeyInfo(activityContext, attr));
                            } catch (IllegalArgumentException ex) {
                                Log.w(LOG_TAG, "Invalid action key for " +
                                        cName.flattenToShortString() + ": " + ex.getMessage());
                                return null;
                            }
                        }
                    }
                }
                tagType = xml.next();
            }
        } catch (XmlPullParserException e) {
            Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e);
            return null;
        } catch (IOException e) {
            Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e);
            return null;
        }
        
        return result;
    }

内部会去解析activity中注册的meta-data对象,找到对应的searchable.xml并解析里面的信息

到此就完成了获取SearchManager->获取activitylist->获取activityinfo->获取解析xml

在附上两张时序图,有错误欢迎指正:

Searchablelist初始化时序图

获取searchablelist数据时序图(获取SearchInfo的数据后可以通过提供的authority来使用对应的contentprovider执行search操作):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值