Android Q Settings模块新特性梳理

 

Android Q Settings模块新特性梳理

1.代码结构差异

Android Q上Settings模块与之前P版本上的Settings模块最直观的差一点是代码结构上的差异。Q上将Settings中会使用到的特有的控件,会移动到SettingsLib下面,为每一种特有控件创建一个目录,并且创建android.bp文件,将widget编译成jar包文件,然后在SettingsLib的android.bp文件中引用这些jar包。比如:

LayoutPreference,SettingsSpinner,AppPreference等等

Android Q Settings模块新特性梳理

 

 

 

 

以LayoutPreference为例:其目录下有res, src,android.bp和AndroidManifest.xml文件,在Android.bp中定义编译完成后生成SettingsLibLayoutPreference

 

Android Q Settings模块新特性梳理

 

LayoutPreference中的android.bp文件,编译后会生成.jar

Android Q Settings模块新特性梳理

 

 

在SettingsLib中的android.bp文件中会使用到该jar文件,

Android Q Settings模块新特性梳理

 

其他的控件,包括SettingsSpinner,ProgressBar,SearchWidget,AppPerference等,也都是这样的结构。

 

2. 一级菜单加载方式差异

Android Q上Settings模块与之前P版本另一个较大差异点,在于主菜单加载方式的差异。Q上相当于在P的基础上,新增了一套加载逻辑,并且P上的加载逻辑还生效。

 

Android P上一级菜单加载流程:

通过PMS查询action为com.android.settings.action.SETTINGS,并且有meta-data携带有com.android.settings.category.ia.home的Activity组件,解析其数据(title,fragment,icon,等),

 

将其封装未Tile类型的数据,Tile是SettingsLib中定义的数据结构类,并将其存储在对应的DashboardCategory集合中.Settings启动的时候,SettingsActivity会加载主菜单布局,此时主菜单布局Content view部分其实是一个RecycleView,该RecycleView中会使用DashboardAdapter加载数据和item布局,DashboardAdapter是通过解析上一步存储的DashboardCategory集合中Tile对象获取数据的。而RecycleView中的item的布局是在Settings中定义的布局,类似于Preference的布局,却不是使用的preference布局。

 

Android Q上一级菜单加载流程:

首先,Q上Settings一级菜单的Content view已经不再是RecycleView,而是一个FrameLayout,后面会将TopLevelSettings(继承与DashboardFragment,最终是继承PreferenceFragment)加到该FrameLayout中。

其次,Settings代码中已经找不到DashboardAdapter这个类,因不再使用RecycleView了,因此不需要通过Adapter加载数据。

最后,Settings主界面的item,Q上是使用的Preference,因此item的布局和preference完全相同(当然,这个也可针对一级菜单单独修改)。

 

Android Q上一级菜单加载流程分析:

首先,从Settings的AndroidManifest.xml中开始:

 

Android Q Settings模块新特性梳理

 

 

点击Launcher中的Settings图标,会打开别名为Settings的Activity,其实际目标Activity是SettingsHomepageActivity,接着查看SettingsHomepageActivity:

Android Q Settings模块新特性梳理

SettingsHomepageActivity 的onCreate方法中,依次加载了主菜单父布局,设置了toolbar上的搜索框,加载显示建议的父布局,加载设置主界面TopLevelSettings到main_container中,继续查看TopLevelSettings:

 

Android Q Settings模块新特性梳理

从TopLevelSettings中可获得两点关键信息,

1. TopLevelSettings的父类是DashboardFragment,DashboardFragment的父类是SettingsPreferenceFragment,这也就是为什么后面能将构建的Preference直接加到布局中。

2. getPreferenceScreenResId()方法,返回的是top_level_settings.xml,但是TopLevelPreference中并没有再复写DashboardFragment其他的构造方法,因此,在上一步中new TopLevelPreference往layout_container中加载的时候,其实是优先走DashboardFragment的构造方法和生命周期方法。

在查看DashboardFragment之前,先看下top_level_settings.xml

 

Android Q Settings模块新特性梳理

 

接着查看DashboardFragment:

Android Q Settings模块新特性梳理

首先看onCreate方法,首次进入时,icicle是null,因此onCreate方法中是不会走到updatePreferenceStates()方法中的,updatePreferenceStates()主要是通过每个Preference item的contoller来更新其summary显示。

 

继续查看其onCreatePreferences()方法:

Android Q Settings模块新特性梳理

onCreatePreferences()中调用了refreshAllPreferences()方法,注意此处传入的getLogTag(),其实是调用的子类TopLevelSettings中实现的getLogTag(),返回的是TopLevelSettings,TopLevelSettings会在后面加载数据的时候用到。

 

查看refreshAllPreferences()方法:

Android Q Settings模块新特性梳理

refreshAllPreferences()中有两个关键方法,一个是displayResourceTiles(),这个方法是从xml中加载布局,对于主界面而言,此处是加载top_level_settings布局。

另一个方法,refreshDashboareTiles()这个方法是通过解析对应的DashboardCategory中的tile对象,动态创建Preference并加到布局中。这个流程,与Android P主菜单加载流程相同。

 

查看displayResourceTiles()方法:

Android Q Settings模块新特性梳理

 

查看refreshDashboardTiles()方法:

Android Q Settings模块新特性梳理

 

refreshDashboardTiles 方法比较长,其功能主要有一下几点:

1. 通过getTilesForCategory()获取与当前category key一直的DashboadCategory对象category(不是集合,可以理解为集合),在category中存储了声明了ia.home的tile对象。

2. 遍历category中存储的tiles,构建preference,通过bindPreferenceToTile方法,将tile中信息与Preference绑定,并将preference添加到PreferenceScreen中,这样preference就被动态添加到一级界面中的DashboardFragment中了。

 

接下来分析getTilesForCategory()方法,在分析getTilesForCategory()之前,需要先分析getCategoryKey()方法:

 

Android Q Settings模块新特性梳理

这个方法是直接从DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP中获取keygetClass().getName()的value值,而.PARENT_TO_CATEGORY_KEY_MAP是在DashboardFragmentRegistry中定义的静态键值对。

Android Q Settings模块新特性梳理

如一级菜单是fragment是从TopLevelSettings,因此返回的是Category.CATEGORY_HOMEPAGE,即com.android.settings.category.ia.home,之后getTilesForCategory()逻辑就与android P相同了,会调用DashboardFeatureProvider的实现类DashboardFeatureProviderImpl的getTilesForCategory()方法,查看Category.CATEGORY_HOMEPAGE的DashboardCategory对象是否存在,不存在的话重新调用PMS查找对应的activity组件,创建tile对象,加到对应的DashboardCategory中,返回DashboardCategory对象。

 

接着继续分析DashboardFeatureProvider.bindPreferenceToTile()方法,DashboardFeatureProvider的实现类DashboardFeatureProviderImpl,分析DashboardFeatureProviderImpl的bindPreferenceToTile()

 

查看DashboardFeatureProviderImpl的bindPreferenceToTile() :

 

Android Q Settings模块新特性梳理

 

 

至此,一级界面就会显示出来了,当然,这个流程并非仅仅只有一级菜单会使用,所有继承与DashboardFragment的子类显示时,都是这个流程,比如:无线和网络,应用和通知等等。

 

3. Android Q上设置搜索功能差异点

设置搜索功能,在Android Q上,BasePreferenceContreller中isAvailable()返回值,新增了一种类型,AVAILABLE_UNSEARCHABLE,BasePreferenceContreller的子类中isAvailable()若返回的是AVAILABLE_UNSEARCHABLE,则这个Preference在界面上是显示的,但是不会被加入到设置搜索数据库中。

BasePreferenceContreller.java中声明AVAILABLE_UNSEARCHABLE的地方:

Android Q Settings模块新特性梳理

AVAILABLE_UNSEARCHABLE 真正起作用的地方是在BasePreferenceContreller的updateNonIndexableKeys()方法中。

Android Q Settings模块新特性梳理

这块判断逻辑是:当前preference处于不可显示状态或者可现实无法搜索时,将pref的key添加到list中去,在初始化搜索数据库时,会将这个list中的数据过滤掉,不往搜索数据库写入数据。

 

4. 设置搜索功能新增settings:searchable属性

Android Q上Settings中新增了自定义属性settings:searchable,settings:searchable=true时设置搜索框中可搜索到,settings:searchable=false无法搜索到。

如:display_settings.xml

Android Q Settings模块新特性梳理

其功能实现的部分在BaseSearchIndexProvider中,在其getNonIndexableKeysFromXml()中会解析xml文件,获取settings:searchable属性,为false时,将其加入到NonIndex的List中,

Android Q Settings模块新特性梳理