0. 一些基本属性
# 数据库 - search_index.db
# 数据表 - prefs_index
1. 数据库初始化
数据库的初始化起初以为是在Settings中进行的,在Settings模块中也找到了数据库初始化代码。但是通过第二步搜索数据步骤发现,搜索数据查询的数据库和在Settings模块中初始化的数据库不是同一个。
数据库的初始化操作其实是在SettingsIntelligence模块中进行的。
1.1 流程图如下

1.2 具体代码
首先是Settings模块,在SettingsActivity中对搜索框进行初始化。在初始化方法中,会对Toolbar设置点击监听,只有在Toolbar点击之后才会触发初始化以及后续操作。
在点击监听中,有一个疑似方法 indexSliceDataAsync() ,通过代码追踪发现也是对数据库进行操作的,会有初始化数据库的动作。但是在第二步的搜索数据过程中发现,这里操作的数据库与查找的数据库不是同一个。所以这里是比较具有迷惑性的。
具体的数据库初始化是在点击事件触发后,跳转界面完成的。
SettingsActivity#onCreate()
final Toolbar toolbar = findViewById(R.id.search_action_bar);
FeatureFactory.getFactory(this).getSearchFeatureProvider()
.initSearchToolbar(this, toolbar);
setActionBar(toolbar);
SearchFeatureProvider#initSearchToolbar()
// 为Toolbar设置点击事件
toolbar.setOnClickListener(tb -> {
// new Intent("com.android.settings.action.SETTINGS_SEARCH");
final Intent intent = SEARCH_UI_INTENT;
// com.android.settings.intelligence
intent.setPackage(getSettingsIntelligencePkgName());
FeatureFactory.getFactory(
activity.getApplicationContext()).getSlicesFeatureProvider()
.indexSliceDataAsync(activity.getApplicationContext());
activity.startActivityForResult(intent, 0 /* requestCode */);
});
点击Toolbar之后,会进行跳转。进入到搜索页面,这个页面属于SettingsIntelligence模块,从Intent中可以找到对应的Activity.
SearchActivity#onCreate()
fragmentManager.beginTransaction()
.add(R.id.main_content, new SearchFragment())
.commit();
SearchFragment#onCreate()
mSearchFeatureProvider.updateIndexAsync(getContext(), this /* indexingCallback */);
updateIndexAsync是去初始化数据库的操作。
SearchFeatureProviderImpl#updateIndexAsync()
getIndexingManager(context).indexDatabase(callback);
DatabaseIndexingManager#indexDatabase()
IndexingTask task = new IndexingTask(callback);
task.execute();
在DatabaseIndexingManager中开启一个异步任务,去执行初始化操作。IndexingTask是DatabaseIndexingManager中的内部类。
IndexingTask#doInBackground()
performIndexing();
DatabaseIndexingManager#performIndexing()
// action:android.content.action.SEARCH_INDEXABLES_PROVIDER
final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
// 获得所需的ContentProvider
final List<ResolveInfo> providers =
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, providers);
if (isFullIndex) {
rebuildDatabase();
}
// 从Provider中解析数据
PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);
final long updateDatabaseStartTime = System.currentTimeMillis();
// 将解析出来的数据存入到数据库中
updateDatabase(indexData, isFullIndex);
IndexDatabaseHelper.setIndexed(mContext, providers);
在performIndexing方法中,通过Intent获得所需的providers,之后遍历providers获得要存入到数据的数据。得到数据后,将数据存入到数据库。
Settings中对应的ContentProvider
Settings#AndroidManifest.xml
<provider
android:name=".search.SettingsSearchIndexablesProvider"
android:authorities="com.android.settings"
android:multiprocess="false"
android:grantUriPermissions="true"
android:permission="android.permission.READ_SEARCH_INDEXABLES"
android:exported="true">
<intent-filter>
<action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
</intent-filter>
</provider>
从ContentProvider中解析数据
DatabaseIndexingManager#getIndexDataFromProviders()
if (mCollector == null) {
mCollector = new PreIndexDataCollector(mContext);
}
return mCollector.collectIndexableData(providers, isFullIndex);
PreIndexDataCollector#collectIndexableData()
mIndexData = new PreIndexData();
for (final ResolveInfo info : providers) {
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
if (SearchFeatureProvider.DEBUG) {
final long nonIndexableTime = System.currentTimeMillis() - nonIndexableStartTime;
Log.d(TAG, "performIndexing update non-indexable for package " + packageName
+ " took time: " + nonIndexableTime);
}
}
return mIndexData;
将解析出的数据保存到数据库
DatabaseIndexingManager#updateDatabase()
// 从PreIndexData中解析出要存入到数据库中样式的数据
final List<IndexData> indexData = getIndexData(preIndexData);
insertIndexData(database, indexData);
insertSiteMapData(database, getSiteMapPairs(indexData, preIndexData.getSiteMapPairs()));
if (!isFullIndex) {
updateDataInDatabase(database, nonIndexableKeys);
}
insertIndexData()
for (IndexData dataRow : indexData) {
values = new ContentValues();
values.put(DATA_TITLE, dataRow.updatedTitle);
values.put(DATA_TITLE_NORMALIZED, dataRow.normalizedTitle);
values.put(DATA_SUMMARY_ON, dataRow.updatedSummaryOn);
values.put(DATA_SUMMARY_ON_NORMALIZED, dataRow.normalizedSummaryOn);
values.put(DATA_ENTRIES, dataRow.entries);
values.put(DATA_KEYWORDS, dataRow.spaceDelimitedKeywords);
values.put(DATA_PACKAGE, dataRow.packageName);
values.put(CLASS_NAME, dataRow.className);
values.put(SCREEN_TITLE, dataRow.screenTitle);
values.put(INTENT_ACTION, dataRow.intentAction);
values.put(INTENT_TARGET_PACKAGE, dataRow.intentTargetPackage);
values.put(INTENT_TARGET_CLASS, dataRow.intentTargetClass);
values.put(ICON, dataRow.iconResId);
values.put(ENABLED, dataRow.enabled);
values.put(DATA_KEY_REF, dataRow.key);
values.put(PAYLOAD_TYPE, dataRow.payloadType);
values.put(PAYLOAD, dataRow.payload);
database.replaceOrThrow(TABLE_PREFS_INDEX, null, values);
}
insertSiteMapData()
for (SiteMapPair pair : siteMapPairs) {
database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_SITE_MAP,
null /* nullColumnHack */, pair.toContentValue());
}
updateDataInDatabase()
database.update(TABLE_PREFS_INDEX, enabledToDisabledValue, whereClause, null);
database.update(TABLE_PREFS_INDEX, disabledToEnabledValue, whereClause, null);
首先会将数据再解析一遍,解析为IndexData的数据集,之后存入到数据库。
getIndexData()
return mConverter.convertPreIndexDataToIndexData(data);
IndexDataConverter#convertPreIndexDataToIndexData()
List<SearchIndexableData> indexableData = preIndexData.getDataToUpdate();
// 遍历集合,根据对象的不同,采取不同的解析方式
for (SearchIndexableData data : indexableData) {
if (data instanceof SearchIndexableRaw) {
final IndexData convertedRaw = convertRaw(mContext, rawData, rawNonIndexableKeys);
indexData.add(convertedRaw);
} else if (data instanceof SearchIndexableResource) {
// 具体的实现是去解析 sir.xmlResId 这个XML文件
final List<IndexData> resourceData = convertResource(sir, resourceNonIndexableKeys);
indexData.addAll(resourceData);
}
}
数据初始化到此结束
2. 搜索数据
2.1流程图

2.2 代码
搜索数据起点是在点击搜索框,输入数据之后。位置是在SettingsIntelligence模块中。
SearchFragment#onQueryTextChange()
boolean isEmptyQuery = TextUtils.isEmpty(query);
if (isEmptyQuery) {
......
} else {
restartLoaders();
}
restartLoaders()
final LoaderManager loaderManager = getLoaderManager();
loaderManager.restartLoader(SearchLoaderId.SEARCH_RESULT, null /* args */, this /* callback */);
LoaderManager的代码在frameworks下,在执行完成后会执行回调onCreateLoader
SearchFragment#onCreateLoader()
switch (id) {
case SearchLoaderId.SEARCH_RESULT:
return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);
}
SearchFeatureProviderImpl#getSearchResultLoader()
return new SearchResultLoader(context, cleanQuery(query));
SearchResultLoader#loadInBackground()
SearchResultAggregator aggregator = SearchResultAggregator.getInstance();
return aggregator.fetchResults(getContext(), mQuery);
SearchResultAggregator#fetchResults()
// 开启任务执行查询操作
final List<SearchQueryTask> tasks =
mFeatureProvider.getSearchQueryTasks(context, query);
// Start tasks
for (SearchQueryTask task : tasks) {
executorService.execute(task);
}
// 收集查询的结果
final Map<Integer, List<? extends SearchResult>> taskResults = new ArrayMap<>();
for (SearchQueryTask task : tasks) {
final int taskId = task.getTaskId();
taskResults.put(taskId, task.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
// 合并结果集
final List<? extends SearchResult> mergedResults = mergeSearchResults(taskResults);
return mergedResults;
获取所有的查询任务
SearchFeatureProviderImpl#getSearchQueryTasks()
final List<SearchQueryTask> tasks = new ArrayList<>();
final String cleanQuery = cleanQuery(query);
tasks.add(DatabaseResultTask.newTask(context, getSiteMapManager(), cleanQuery));
tasks.add(InstalledAppResultTask.newTask(context, getSiteMapManager(), cleanQuery));
tasks.add(AccessibilityServiceResultTask.newTask(context, getSiteMapManager(), cleanQuery));
tasks.add(InputDeviceResultTask.newTask(context, getSiteMapManager(), cleanQuery));
return tasks;
这里可以看到为查询任务集合里添加了4个任务,以数据库查询为例。执行查询操作。
SearchQueryTask.QueryWorker#call()
return query();
DatabaseResultTask#query()
final Set<SearchResult> resultSet = new HashSet<>();
resultSet.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));
resultSet.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));
resultSet.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));
resultSet.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));
List<SearchResult> resultList = new ArrayList<>(resultSet);
Collections.sort(resultList);
return resultList;
在数据库任务查询中,添加了4条查询语句。分别对应的whereClause语句如下
| method | whereCaluse | selection |
|---|---|---|
| firstWordQuery | (data_title like ? OR data_title_normalized like ? ) AND enabled = 1 | [输入%, 输入%] |
| secondaryWordQuery | (data_title like ? OR data_title_normalized like ? ) AND enabled = 1 | [% 输入%, % 输入%] |
| anyWordQuery | (data_summary_on like ? OR data_summary_on like ? OR data_summary_on_normalized like ? OR data_summary_on_normalized like ? OR data_summary_off like ? OR data_summary_off like ? OR data_summary_off_normalized like ? OR data_summary_off_normalized like ?) AND enabled = 1 | [输入%, % 输入%, 输入%, % 输入%, 输入%, % 输入%, 输入%, % 输入%] |
| anyWordQuery | whereClause = (data_keywords like ? OR data_keywords like ? OR data_entries like ? OR data_entries like ?) AND enabled = 1 | [输入%, % 输入%, 输入%, % 输入%] |
查询结束后,会合并结果集
SearchResultAggregator#mergeSearchResults()
final List<SearchResult> searchResults = new ArrayList<>();
searchResults.addAll(taskResults.remove(DatabaseResultTask.QUERY_WORKER_ID));
final PriorityQueue<SearchResult> heap = new PriorityQueue<>();
for (List<? extends SearchResult> taskResult : taskResults.values()) {
heap.addAll(taskResult);
}
while (!heap.isEmpty()) {
searchResults.add(heap.poll());
}
return searchResults;
结果集的操作完成之后,会通过LoadManager产生回调onLoadFinished。之后将结果集发送给适配器,由适配器来做数据的展示。
SearchFragment#onLoadFinished()
mSearchAdapter.postSearchResults(data);
3. 结束
关于Settings的数据初始化和搜索流程分析到此结束。