当前位置: 首页 > news >正文

Settings搜索系统SettingsIntelligence

目录

概要

代码流程图

代码流程分析

1.SettingsHomepageActivity.java

2.SearchFeatureProvider.java

3、SearchFeatureProviderImpl.java

4、SearchActivity.java

5.SearchFragment.java

6.SettingsIntelligence-SearchFeatureProviderImpl.java

7.DatabaseIndexingManager.java

8.PreIndexDataCollector.java

9.IndexDatabaseHelper.java

小结


概要

在Android系统中,Setting菜单非常多,有些菜单很难找到,因此Google支持搜索菜单功能。搜索的主要逻辑在packages/apps/SettingsIntelligence模块中。模块包名为com.android.settings.intelligence。

该模块会检索所有继承自SearchIndexablesProvider的ContentProvider,将所有数据保存至数据库

/data/data/com.android.settings.intelligence/databases/search_index.db。然后在SearchFragment中通过访问该数据库来实现搜索菜单的功能。本文重点探讨保存数据库这步流程

代码流程图

第一次从设置首页进入搜索时,会跳转到SettingsIntelligence加载数据库,以下为这个过程的流程图

代码流程分析

接下来,我会按照流程图的顺序,逐步分析相关代码,所有代码示例皆来自Android14

1.SettingsHomepageActivity.java

这个为设置首页,从这个界面顶端进入搜索

SettingsHomepageActivity.java:@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initSearchBarView();  //1.搜索相关的初始化
}private void initSearchBarView() {final Toolbar toolbar = findViewById(R.id.search_action_bar); //获取search控件//通过Factory模式,将搜索初始化放在SearchFeatureProvider中实现FeatureFactory.getFactory(this).getSearchFeatureProvider() .initSearchToolbar(this /* activity */, toolbar,  SettingsEnums.SETTINGS_HOMEPAGE);if (mIsEmbeddingActivityEnabled) {   //支持分栏的设备会走这里final Toolbar toolbarTwoPaneVersion = findViewById(R.id.search_action_bar_two_pane);FeatureFactory.getFactory(this).getSearchFeatureProvider().initSearchToolbar(this /* activity */, toolbarTwoPaneVersion,SettingsEnums.SETTINGS_HOMEPAGE);}}

2.SearchFeatureProvider.java

主要处理Setting Search相关逻辑,具体实现类为SearchFeatureProviderImpl

SearchFeatureProvider.javadefault void initSearchToolbar(FragmentActivity activity, Toolbar toolbar, int pageId) {final Context context = activity.getApplicationContext();//2.获取SearchActivity的Intent,用于跳转final Intent intent = buildSearchIntent(context, pageId)  .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//增加OnClickListener,点击搜索控件时,会回调onClick跳转到SearchActivitytoolbar.setOnClickListener(tb -> startSearchActivity(context, activity, pageId, intent));}private static void startSearchActivity(Context context, FragmentActivity activity, int pageId, Intent intent) {FeatureFactory.getFactory(context).getSlicesFeatureProvider().indexSliceDataAsync(context);FeatureFactory.getFactory(context).getMetricsFeatureProvider().logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId);final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();//3.start SearchActivityactivity.startActivity(intent, bundle);}

3、SearchFeatureProviderImpl.java

从这里跳转到SearchActivity

SearchFeatureProviderImpl.java@Overridepublic Intent buildSearchIntent(Context context, int pageId) {return new Intent(Settings.ACTION_APP_SEARCH_SETTINGS) //获取SearchActivity action.setPackage(getSettingsIntelligencePkgName(context)) //获取包名.putExtra(Intent.EXTRA_REFERRER, buildReferrer(context, pageId));}//获取的包名为com.android.settings.intelligence
default String getSettingsIntelligencePkgName(Context context) {return context.getString(R.string.config_settingsintelligence_package_name);}Intent.java//SearchActivity会配置该action@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";

4、SearchActivity.java

Search界面

SettingsIntelligence AndroidManifest.xml<activityandroid:name=".search.SearchActivity"android:exported="true"android:theme="@style/Theme.Settings.NoActionBar"android:windowSoftInputMode="adjustResize"><intent-filter priority="-1"><!--通过匹配这个action来跳转SearchActivity--><action android:name="com.android.settings.action.SETTINGS_SEARCH" /><action android:name="android.settings.APP_SEARCH_SETTINGS" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity>
public class SearchActivity extends FragmentActivity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.search_main);FragmentManager fragmentManager = getSupportFragmentManager();Fragment fragment = fragmentManager.findFragmentById(R.id.main_content);if (fragment == null) {fragmentManager.beginTransaction().add(R.id.main_content, new SearchFragment())  //4.嵌入SearchFragment,主要逻辑在Fragment里面.commit();}}@Overridepublic boolean onNavigateUp() {finish();return true;}
}

5.SearchFragment.java

搜索界面,搜索逻辑从这里开始调用

SearchFragment.java@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);long startTime = System.currentTimeMillis();setHasOptionsMenu(true);final LoaderManager loaderManager = getLoaderManager();//给搜索结果使用的数据mSearchAdapter = new SearchResultsAdapter(this /* fragment */);mSavedQueryController = new SavedQueryController(getContext(), loaderManager, mSearchAdapter);mSearchFeatureProvider.initFeedbackButton();if (savedInstanceState != null) {//保存搜索结果mQuery = savedInstanceState.getString(SearchCommon.STATE_QUERY);mNeverEnteredQuery = savedInstanceState.getBoolean(SearchCommon.STATE_NEVER_ENTERED_QUERY);mShowingSavedQuery = savedInstanceState.getBoolean(SearchCommon.STATE_SHOWING_SAVED_QUERY);} else {mShowingSavedQuery = true;}5.加载搜索数据库逻辑从这里开始mSearchFeatureProvider.updateIndexAsync(getContext(), this /* indexingCallback */);   //load database 1if (SearchFeatureProvider.DEBUG) {Log.d(TAG, "onCreate spent " + (System.currentTimeMillis() - startTime) + " ms");}}//IndexingTask数据处理完成后onPostExecute会通过mCallback.onIndexingFinished()进行回调返回这里@Overridepublic void onIndexingFinished() {   //load database 完成后回调if (getActivity() == null) {return;}if (mShowingSavedQuery) {mSavedQueryController.loadSavedQueries();} else {final LoaderManager loaderManager = getLoaderManager();loaderManager.initLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT, null /* args */,this /* callback */);}requery();}

6.SettingsIntelligence-SearchFeatureProviderImpl.java

此处为SettingsIntelligence中的类,和前面的不是同一个

/*** FeatureProvider for the refactored search code.*/
SearchFeatureProviderImpl.java@Overridepublic void updateIndexAsync(Context context, IndexingCallback callback) {  //load database 2if (DEBUG) {Log.d(TAG, "updating index async");}
//6.此处就是一个衔接的作用,最终要调用DatabaseIndexingManager中进行处理getIndexingManager(context).indexDatabase(callback);  }

7.DatabaseIndexingManager.java

DatabaseIndexingManager.IndexingTask

* Consumes the SearchIndexableProvider content providers.
* Updates the Resource, Raw Data and non-indexable data for Search.

该文件主要是检索分析所有SearchIndexableProvider的之类,将数据分类保存到数据库/data/data/com.android.settings.intelligence/databases

IndexingTask内部类则用于异步处理保存数据库的耗时操作
DatabaseIndexingManager.java
/*** Consumes the SearchIndexableProvider content providers.* Updates the Resource, Raw Data and non-indexable data for Search.
*///7.将数据库加载的耗时操作放入IndexingTask
public void indexDatabase(IndexingCallback callback) { IndexingTask task = new IndexingTask(callback);task.execute();}public void performIndexing() {   //load database 5 关键代码final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);  //查询所有search数据的intent,该intent为"android.content.action.SEARCH_INDEXABLES_PROVIDER"final List<ResolveInfo> providers =mContext.getPackageManager().queryIntentContentProviders(intent, 0);  //查询所有该intent匹配的Provider//检查是否要全部更新,如修改语言,OTA后final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, providers);  if (isFullIndex) {rebuildDatabase();  //若要全部更新,则删除数据库Table,重建Table}//9.收集数据,返回格式为indexDataPreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);   final long updateDatabaseStartTime = System.currentTimeMillis();updateDatabase(indexData, isFullIndex);    //load database 7   更新数据库//设一些标志位,可能会用于判断isFullIndex,结果保存在indexing_manager.xml,格式为
SharedPreferences,包括语言,包括搜索数据的应用包名和fingerprintIndexDatabaseHelper.setIndexed(mContext, providers);   if (DEBUG) {final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime;Log.d(TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime);}}//9.搜集搜索数据,返回格式为PreIndexData 
@VisibleForTestingPreIndexData getIndexDataFromProviders(List<ResolveInfo> providers, boolean isFullIndex) {if (mCollector == null) {mCollector = new PreIndexDataCollector(mContext);}
//9.搜集搜索数据,返回格式为PreIndexData return mCollector.collectIndexableData(providers, isFullIndex); }//数据返回后,更新数据库@VisibleForTestingvoid updateDatabase(PreIndexData preIndexData, boolean isFullIndex) { final SQLiteDatabase database = getWritableDatabase();  //IndexDatabaseHelper用于管理数据库
.
.
.insertIndexData(database, indexData);  //10.插入数据库,插入成功后,则数据库更新完成}private void insertIndexData(SQLiteDatabase database, List<IndexData> indexData) {  //load database 7.3ContentValues values;for (IndexData dataRow : indexData) {if (TextUtils.isEmpty(dataRow.normalizedTitle)) {continue;}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(DATA_AUTHORITY, dataRow.authority);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);values.put(TOP_LEVEL_MENU_KEY, dataRow.topLevelMenuKey);//database类为IndexDatabaseHelperdatabase.replaceOrThrow(TABLE_PREFS_INDEX, null, values);  //写入数据库}}IndexingTask classpublic class IndexingTask extends AsyncTask<Void, Void, Void> {@VisibleForTestingIndexingCallback mCallback;private long mIndexStartTime;public IndexingTask(IndexingCallback callback) {mCallback = callback;}@Overrideprotected void onPreExecute() {mIndexStartTime = System.currentTimeMillis();mIsIndexingComplete.set(false);}//8.马上进入真正的保存数据库操作@Overrideprotected Void doInBackground(Void... voids) {performIndexing();   //load database 4return null;}@Overrideprotected void onPostExecute(Void aVoid) {int indexingTime = (int) (System.currentTimeMillis() - mIndexStartTime);FeatureFactory.get(mContext).metricsFeatureProvider(mContext).logEvent(SettingsIntelligenceLogProto.SettingsIntelligenceEvent.INDEX_SEARCH,indexingTime);mIsIndexingComplete.set(true);if (mCallback != null) {mCallback.onIndexingFinished();   //执行完成后,此处将结果回调给SearchFragment}}}

8.PreIndexDataCollector.java

在此类中处理数据

PreIndexDataCollector.java//9.处理返回数据
public PreIndexData collectIndexableData(List<ResolveInfo> providers, boolean isFullIndex) {mIndexData = new PreIndexData();for (final ResolveInfo info : providers) {if (!isWellKnownProvider(info)) {continue;}final String authority = info.providerInfo.authority;final String packageName = info.providerInfo.packageName;if (isFullIndex) {addIndexablesFromRemoteProvider(packageName, authority);   //加载数据}final long nonIndexableStartTime = System.currentTimeMillis();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;  //返回结果}

9.IndexDatabaseHelper.java

数据库创建读写操作都在这里

IndexDatabaseHelper.javapublic class IndexDatabaseHelper extends SQLiteOpenHelper {//这些Table名称
public interface Tables {String TABLE_PREFS_INDEX = "prefs_index";String TABLE_SITE_MAP = "site_map";String TABLE_META_INDEX = "meta_index";String TABLE_SAVED_QUERIES = "saved_queries";}
//数据库中保存的数据都在这里,包括title,Summary,package,class name,key,icon等
public interface IndexColumns {String DATA_TITLE = "data_title";String DATA_TITLE_NORMALIZED = "data_title_normalized";String DATA_SUMMARY_ON = "data_summary_on";String DATA_SUMMARY_ON_NORMALIZED = "data_summary_on_normalized";String DATA_SUMMARY_OFF = "data_summary_off";String DATA_SUMMARY_OFF_NORMALIZED = "data_summary_off_normalized";String DATA_ENTRIES = "data_entries";String DATA_KEYWORDS = "data_keywords";String DATA_PACKAGE = "package";String DATA_AUTHORITY = "authority";String CLASS_NAME = "class_name";String SCREEN_TITLE = "screen_title";String INTENT_ACTION = "intent_action";String INTENT_TARGET_PACKAGE = "intent_target_package";String INTENT_TARGET_CLASS = "intent_target_class";String ICON = "icon";String ENABLED = "enabled";String DATA_KEY_REF = "data_key_reference";String PAYLOAD_TYPE = "payload_type";String PAYLOAD = "payload";String TOP_LEVEL_MENU_KEY = "top_level_menu_key";}@Overridepublic void onCreate(SQLiteDatabase db) {bootstrapDB(db);}//会创建以下几个Tableprivate void bootstrapDB(SQLiteDatabase db) {db.execSQL(CREATE_INDEX_TABLE);db.execSQL(CREATE_META_TABLE);db.execSQL(CREATE_SAVED_QUERIES_TABLE);db.execSQL(CREATE_SITE_MAP_TABLE);db.execSQL(INSERT_BUILD_VERSION);Log.i(TAG, "Bootstrapped database");}public void reconstruct(SQLiteDatabase db) {    //load database 5.2mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE).edit().clear().commit();dropTables(db);   //删除数据库的表tablebootstrapDB(db);   //新建数据库的表}
}

小结

Settings属于Android原生比较大的模块了,代码也非常多,这只是其中搜索功能的一部分。其他部分将在其他文章中继续探讨

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 数说故事 | 大数据洞察宠物消费市场数据
  • java-静态工具类获取YAML配置文件中的值
  • Vue 模版编译原理
  • git fetch和 pull的区别
  • Java基本语法学习的案例练习
  • nginx续1:
  • 商家转账到零钱开通最快捷径
  • 为什么阿里开发手册不建议使用Date类?
  • rust 初探 -- 路径(path)
  • 嵌入式必备知识总结(一)
  • 多模态大模型
  • 基于Laravel打造,模块化开发框架助力项目快速落地 Modstart
  • 黑马JavaWeb后端案例开发(包含所有知识点!!!)
  • Serverless Knative冷启动与自动扩缩容研究:从原理到实践
  • 数据结构之《栈》
  • 【node学习】协程
  • co.js - 让异步代码同步化
  • Flannel解读
  • Less 日常用法
  • SegmentFault 2015 Top Rank
  • Solarized Scheme
  • use Google search engine
  • 大主子表关联的性能优化方法
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 微信公众号开发小记——5.python微信红包
  • 异常机制详解
  • 再次简单明了总结flex布局,一看就懂...
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ‌内网穿透技术‌总结
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • #1015 : KMP算法
  • #if #elif #endif
  • (12)Hive调优——count distinct去重优化
  • (pojstep1.3.1)1017(构造法模拟)
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (二)原生js案例之数码时钟计时
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • ... 是什么 ?... 有什么用处?
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net mvc部分视图
  • .net SqlSugarHelper
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • /usr/bin/perl:bad interpreter:No such file or directory 的解决办法
  • [ 第一章] JavaScript 简史
  • [100天算法】-实现 strStr()(day 52)
  • [Ariticle] 厚黑之道 一 小狐狸听故事
  • [BSGS算法]纯水斐波那契数列