Android4.4设置源码分析(一):设置主界面与各模块之间的联系
寻找一个apk入口最快捷的途径就是查找AndroidManifest.xml文件,设置的AndroidManifest.xml文件如下:
<application android:label="@string/settings_label"
android:icon="@mipmap/ic_launcher_settings"
android:taskAffinity=""
android:theme="@style/Theme.Settings"
android:hardwareAccelerated="true"
android:requiredForAllUsers="true"
android:supportsRtl="true">
<!-- Settings -->
<activity android:name="Settings"
android:label="@string/settings_label_launcher"
android:taskAffinity="com.android.settings"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.settings.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
这两个属性说明该设置主activity是Settings,对应的类是Settings.java;而进入里表中的某项设置后,不是进入到新的Activity,而是在原来的Activity上切换了一个UI界面而已。
4.0上Settings使用了Framgment机制。Fragment是我们在单个Activity上要切换多 个UI界面,显示不同内容,对不同的界面不再使用不同的Activity。模块化这些UI面板以便提供给其他Acitivity使用便利。同时我们显示的Fragment也会受到当前的这个 Acitivity生命周期影响。在res/xml/settings_headers.xml中可以找到settings中包含的选项, 如:
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- WIRELESS and NETWORKS -->
<header android:id="@+id/wireless_section"
android:title="@string/header_category_wireless_networks" />
<!-- Wifi -->
<header
android:id="@+id/wifi_settings"
android:fragment="com.android.settings.wifi.WifiSettings"
android:title="@string/wifi_settings_title"
android:icon="@drawable/ic_settings_wireless" />
<!-- Bluetooth -->
<header
android:id="@+id/bluetooth_settings"
android:fragment="com.android.settings.bluetooth.BluetoothSettings"
android:title="@string/bluetooth_settings_title"
android:icon="@drawable/ic_settings_bluetooth2" />
<!-- Ethernet -->
<header
android:id="@+id/ethernet_settings"
android:title="@string/eth_setting"
android:icon="@drawable/ic_settings_ethernet"
android:fragment="com.android.settings.ethernet.EthernetSettings"/>
Settings继承自PreferenceActivity,Settings的主界面布局加载地方:
/**
* Populate the activity with the top-level headers.
*/
@Override
public void onBuildHeaders(List<Header> headers) {
if (!onIsHidingHeaders()) {
Log.d(LOG_TAG,"!onIsHidingHeaders");
loadHeadersFromResource(R.xml.settings_headers, headers);
updateHeaderList(headers);
}
}
updateHeaderList(headers)方法有什么用呢?
private void updateHeaderList(List<Header> target) {
final boolean showDev = mDevelopmentPreferences.getBoolean(
DevelopmentSettings.PREF_SHOW,
android.os.Build.TYPE.equals("eng"));
int i = 0;
final UserManager um = (UserManager)
getSystemService(Context.USER_SERVICE);
mHeaderIndexMap.clear();
while (i < target.size()) {
Header header = target.get(i);
// Ids are integers, so downcasting
int id = (int) header.id;
if (id == R.id.operator_settings || id ==
R.id.manufacturer_settings) {
Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove
(this,target, header);
} else if (id == R.id.wifi_settings) {
// Remove WiFi Settings if WiFi service is not available.
if(!getPackageManager().hasSystemFeature
(PackageManager.FEATURE_WIFI)) {
target.remove(i);
}
可以看出,它的作用是根据当前平台是否支持某项功能,决定是否显示相应的选项;Android 3.0之后,摒弃了传统的 PreferenceScreen 嵌套方法,而是采用了所谓的Preference Headers 方法,该方法的要点是:在主屏中通过 headers xml 文件布局列出所有的主题设置项,而每个主题设置的详细设置则
由各自指定的 PreferenceFragment 负责,而各自的 PreferenceFragment 可以如传统的PreferenceActivity一样布局自身的 PreferenceScreen。另外,为了能够显示出 headers 中的布局列表,需要在继承的PreferenceActivity 类中实现 onBuildHeaders() 回调方法。
hasSystemFeature方法
详细讲解hasSystemFeature(PackageManager.FEATURE_WIFI)。
FEATURE_WIFI
frameworks/base/core/java/android/content/pm/PackageManager.java
public static final String FEATURE_WIFI = “android.hardware.wifi”;
hasSystemFeature方法:
frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
public boolean hasSystemFeature(String name) {
synchronized (mPackages) {
return mAvailableFeatures.containsKey(name);
}
}
mAvailableFeatures里面的内容是通过读取/system/etc/permissions下面的文档,如android.hardware.wifi.xml;
android4.0 及以上 版本里 ,如果在settings下看不到wifi和bluetooth两个菜单选项,这是因为在setting里,
对系统是否有特定的模块加上了判断,如果没有就不显示。android4.0的模块判断函数:hasSystemFeature(String string).
通过该函数判断系统是否有特定的模块功能。
例如判断是否有 wifi 和 蓝牙模块的具体代码:
getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
getPackageManager().hasSystemFe(PackageManager.FEATURE_BLUETOOTH);
PackageManager这些字符串 存在system/etc/permissions/xxxx.xml文件里,它们一般从/framework/base/data/etc/xxx.xml复制过来。
PackageManager.FEATURE_BLUETOOTH = "android.hardware.wifi"
PackageManager.FEATURE_BLUETOOTH = "android.hardware.bluetooth"
解决wifi和蓝牙不显示方法:
一、直接把包含对应 feature 的xml文件复制到system/etc/permissions/目录下,相当于加上系统所具有的具体模块的功能配置文件;
如蓝牙不显示,将android.hardware.bluetooth.xml放在system/etc/permissions/目录下即可。
二、修改product_copy.mk文件,添加相应的设备。
参考资料:Android Settings(系统设置)源码分析(一)
参考: