Android4.4设置源码分析(一):设置主界面与各模块之间的联系

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(系统设置)源码分析(一)

参考:

https://yq.aliyun.com/articles/12896

展开阅读全文

没有更多推荐了,返回首页