SettingsIntelligence
- 代码路径
源码:packages/apps/SettingsIntelligence/
编译及安装:单编模块名SettingsIntelligence,可直接安装编译出来的 apk。
3.启动页
此处由设置代码易知,搜索启动如上 activity。接下来查看该界面的加载流程。
- 数据加载
SearchActivity 在 onCreate 方法中加载了 SearchFragment,接下来查看 fragment 中的逻辑
@Override
public void onCreate(Bundle savedInstanceState) {
…
final LoaderManager loaderManager = getLoaderManager();
// 顾名思义,搜索结果适配器
mSearchAdapter = new SearchResultsAdapter(this /* fragment /);
mSavedQueryController = new SavedQueryController(
getContext(), loaderManager, mSearchAdapter);
mSearchFeatureProvider.initFeedbackButton();
…
// 异步加载索引数据
mSearchFeatureProvider.updateIndexAsync(getContext(), this / indexingCallback */);
}
@Override
public void updateIndexAsync(Context context, IndexingCallback callback) {
…
getIndexingManager(context).indexDatabase(callback);
}
public void indexDatabase(IndexingCallback callback) {
// 启动异步任务加载数据
IndexingTask task = new IndexingTask(callback);
task.execute();
}
public class IndexingTask extends AsyncTask<Void, Void, Void> {
…
@Override
protected Void doInBackground(Void… voids) {
performIndexing();
return null;
}
…
}
public static final String PROVIDER_INTERFACE =
“android.content.action.SEARCH_INDEXABLES_PROVIDER”;
public void performIndexing() {
// 查询实现了 android.content.action.SEARCH_INDEXABLES_PROVIDER 的 provider
final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
final List providers =
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
final String localeStr = Locale.getDefault().toString();
final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, providers, localeStr);// 判断是否需要重构数据库
if (isFullIndex) {rebuildDatabase();
}// 从 providers 中解析 indexData
PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);// 更新数据库
updateDatabase(indexData, isFullIndex);
IndexDatabaseHelper.setIndexed(mContext, providers, localeStr);
}
4.1 从 provider 解析数据
PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);
PreIndexData getIndexDataFromProviders(List providers, boolean isFullIndex) {
if (mCollector == null) {
mCollector = new PreIndexDataCollector(mContext);
}
return mCollector.collectIndexableData(providers, isFullIndex);
}
public PreIndexData collectIndexableData(List providers, boolean isFullIndex) {
mIndexData = new PreIndexData();
for (final ResolveInfo info : providers) {// 1,provider 需要添加权限 android.permission.READ_SEARCH_INDEXABLES// 2,实现 provider 的需要是系统应用if (!isWellKnownProvider(info)) {continue;}final String authority = info.providerInfo.authority;final String packageName = info.providerInfo.packageName;// 需要重构数据库时重新加载数据if (isFullIndex) {addIndexablesFromRemoteProvider(packageName, authority);}// 添加不可索引的数据addNonIndexablesKeysFromRemoteProvider(packageName, authority);
}return mIndexData;
}
// 数据加载
private void addIndexablesFromRemoteProvider(String packageName, String authority) {
try {
final Context context = mContext.createPackageContext(packageName, 0);
// 构建 urifinal Uri uriForResources = buildUriForXmlResources(authority);// 根据提供的 provider 查询数据,添加到 mDataToUpdatemIndexData.addDataToUpdate(authority, getIndexablesForXmlResourceUri(context, packageName, uriForResources,SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS));final Uri uriForRawData = buildUriForRawData(authority);mIndexData.addDataToUpdate(authority, getIndexablesForRawDataUri(context, packageName, uriForRawData,SearchIndexablesContract.INDEXABLES_RAW_COLUMNS));//*/ freeme.caoguofeng, 20230713. Settings-search: add for Injection Index.final Uri uriForDynamicRawData = buildUriForDynamicRawData(authority);mIndexData.addDataToUpdate(authority, getIndexablesForRawDataUri(context, packageName, uriForDynamicRawData,SearchIndexablesContract.INDEXABLES_RAW_COLUMNS));//*/final Uri uriForSiteMap = buildUriForSiteMap(authority);mIndexData.addSiteMapPairs(getSiteMapFromProvider(context, uriForSiteMap));
} catch (PackageManager.NameNotFoundException e) {Log.w(TAG, "Could not create context for " + packageName + ": "+ Log.getStackTraceString(e));
}
}
4.2 更新 search_index.dp 数据库
void updateDatabase(PreIndexData preIndexData, boolean isFullIndex) {
// 获取不可索引的数据
final Map<String, Set> nonIndexableKeys = preIndexData.getNonIndexableKeys();
…
try {database.beginTransaction();// Convert all Pre-index data to Index data and and insert to db.List<IndexData> indexData = getIndexData(preIndexData);...// 数据插入insertIndexData(database, indexData);// Only check for non-indexable key updates after initial index.// Enabled state with non-indexable keys is checked when items are first inserted.if (!isFullIndex) {updateDataInDatabase(database, nonIndexableKeys);}database.setTransactionSuccessful();
} finally {database.endTransaction();
}
}
void updateDataInDatabase(SQLiteDatabase database,
Map<String, Set> nonIndexableKeys) {
// 索引是否可用的条件
final String whereEnabled = ENABLED + " = 1";
final String whereDisabled = ENABLED + " = 0";
// 获取之前可用的索引数据
final Cursor enabledResults = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,whereEnabled, null, null, null, null);final ContentValues enabledToDisabledValue = new ContentValues();
enabledToDisabledValue.put(ENABLED, 0);String authority;
// TODO Refactor: Move these two loops into one method.
while (enabledResults.moveToNext()) {authority = enabledResults.getString(enabledResults.getColumnIndexOrThrow(DATA_AUTHORITY));final String key = enabledResults.getString(enabledResults.getColumnIndexOrThrow(DATA_KEY_REF));final Set<String> authorityKeys = nonIndexableKeys.get(authority);// 更新当前不可用的 key// The indexed item is set to Enabled but is now non-indexableif (authorityKeys != null && authorityKeys.contains(key)) {final String whereClause = getKeyWhereClause(key);database.update(TABLE_PREFS_INDEX, enabledToDisabledValue, whereClause, null);}
}
enabledResults.close();// 获取之前禁用的数据
final Cursor disabledResults = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,whereDisabled, null, null, null, null);final ContentValues disabledToEnabledValue = new ContentValues();
disabledToEnabledValue.put(ENABLED, 1);while (disabledResults.moveToNext()) {authority = disabledResults.getString(disabledResults.getColumnIndexOrThrow(DATA_AUTHORITY));final String key = disabledResults.getString(disabledResults.getColumnIndexOrThrow(DATA_KEY_REF));final Set<String> authorityKeys = nonIndexableKeys.get(authority);// The indexed item is set to Disabled but is no longer non-indexable.// We do not enable keys when authorityKeys is null because it means the keys came// from an unrecognized authority and therefore should not be surfaced as results.// 更新为可用if (authorityKeys != null && !authorityKeys.contains(key)) {final String whereClause = getKeyWhereClause(key);database.update(TABLE_PREFS_INDEX, disabledToEnabledValue, whereClause, null);}
}
disabledResults.close();
}
5. 搜索流程
过滤 SearchFragment.java 代码,可以看到
// SearchFragment.java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
…
ActionBar actionBar = activity.getActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
View actionbarLayout = LayoutInflater.from(getContext()).inflate(
R.layout.freeme_layout_actionar_searchview, null);
// 加载了一个 SearchView
actionBar.setCustomView(actionbarLayout);
mSearchView = actionbarLayout.findViewById(R.id.searchView);
mSearchView.setQuery(mQuery, false /* submitQuery */);
mSearchView.setOnQueryTextListener(this);
mSearchView.requestFocus();
…
return view;
}
// SearchFragment.java
@Override
public boolean onQueryTextChange(String query) {
if (TextUtils.equals(query, mQuery)) {
return true;
}
final boolean isEmptyQuery = TextUtils.isEmpty(query);
mNeverEnteredQuery = false;
mQuery = query;
mSearchAdapter.changQuery(mQuery);// If indexing is not finished, register the query text, but don't search.
if (!mSearchFeatureProvider.isIndexingComplete(getActivity())) {return true;
}if (isEmptyQuery) {// 更新视图mSearchAdapter.clearResults();mSearchAdapter.clearbackground();final LoaderManager loaderManager = getLoaderManager();loaderManager.destroyLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT);mShowingSavedQuery = true;mSavedQueryController.loadSavedQueries();mSearchFeatureProvider.hideFeedbackButton(getView());
} else {mMetricsFeatureProvider.logEvent(SettingsIntelligenceEvent.PERFORM_SEARCH);// 重新启动加载器restartLoaders();
}return true;
}
// SearchFragment.java
private void restartLoaders() {
mShowingSavedQuery = false;
final LoaderManager loaderManager = getLoaderManager();
loaderManager.restartLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT,
null /* args /, this / callback */);
}
// SearchFragment.java 创建 SearchResultLoader
@Override
public Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
final Activity activity = getActivity();
switch (id) {case SearchCommon.SearchLoaderId.SEARCH_RESULT:return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);default:return null;
}
}
// SearchFeatureProviderImpl.java
@Override
public SearchResultLoader getSearchResultLoader(Context context, String query) {
return new SearchResultLoader(context, cleanQuery(query));
}
public class SearchResultLoader extends AsyncLoader<List<? extends SearchResult>> {
…
@Override
public List<? extends SearchResult> loadInBackground() {
SearchResultAggregator aggregator = SearchResultAggregator.getInstance();
// 重点看 SearchResultAggregator 的 fetchResults 方法
return aggregator.fetchResults(getContext(), mQuery);
}
…
}
// SearchResultAggregator.java
public class SearchResultAggregator {
…
@NonNull
public synchronized List<? extends SearchResult> fetchResults(Context context, String query) {
final SearchFeatureProvider mFeatureProvider = FeatureFactory.get(context)
.searchFeatureProvider();
final ExecutorService executorService = mFeatureProvider.getExecutorService();
// 获取异步任务并执行,具体 task 可查看 SearchFeatureProviderImpl.java 中的 getSearchQueryTasks 方法// 每个 task 的查询规则可自行查看final List<SearchQueryTask> tasks =mFeatureProvider.getSearchQueryTasks(context, query);// Start tasksfor (SearchQueryTask task : tasks) {executorService.execute(task);}// Collect resultsfinal Map<Integer, List<? extends SearchResult>> taskResults = new ArrayMap<>();final long allTasksStart = System.currentTimeMillis();for (SearchQueryTask task : tasks) {final int taskId = task.getTaskId();try {taskResults.put(taskId,task.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));} catch (TimeoutException | InterruptedException | ExecutionException e) {Log.d(TAG, "Could not retrieve result in time: " + taskId, e);taskResults.put(taskId, Collections.EMPTY_LIST);}}// Merge resultsfinal List<? extends SearchResult> mergedResults = mergeSearchResults(taskResults);// 返回搜索结果return mergedResults;
}// TODO (b/68255021) scale the dynamic search results ranks
private List<? extends SearchResult> mergeSearchResults(Map<Integer, List<? extends SearchResult>> taskResults) {final List<SearchResult> searchResults = new ArrayList<>();// First add db results as a special case// 添加 DatabaseResultTask 返回的数据searchResults.addAll(taskResults.remove(DatabaseResultTask.QUERY_WORKER_ID));// Merge the rest into result list: add everything to heap then pop them out one by one.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;
}
}
// SearchFragment.java 数据加载完成后,渲染 UI
@Override
public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
List<? extends SearchResult> data) {
…
mSearchAdapter.postSearchResults(data);
}