使用:
客户端获取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操作):