Androidシステムのソースコード解析(一)---Settings


本文は博主のために苦労してまとめます.転載は出所を明記してください.http://blog.csdn.net/zrf1335348191/article/details/50837027
最近はAndroidのSettingsソースを研究しています.まずソースのカタログ構造を見てください.967ぐらいの書類は頭が痛くて手がつけられませんか?弁舌さわやかにしてください.
1,初めてSettingsを知った
まず、こんなに多くのファイルは、いったいどのファイルがメインインターフェースですか?Settingsディレクトリの下でAndroid manifest.xmlリストの設定ファイルを見つけて、まず起動するactivityを見つけます.
<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>
設定されたメインインターフェースはSettings.Javaであり、
public class Settings extends PreferenceActivity
        implements ButtonBarHandler, OnAccountsUpdateListener {
         .....
<pre name="code" class="java"> loadHeadersFromResource(R.xml.settings_headers, headers);//    
        
         .....
}
 
  
 

所对应的xml文件为Settings_headers.xml(res\xml\)文件。在此摘列出xml文件的一部分。

<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" />

    <!-- MobileData -->
    <header
        android:id="@+id/mobiledata_settings"
        android:icon="@drawable/stat_notify_mobile_data"
        android:title="@string/data_usage_enable_mobile">
        <intent
            android:action="android.intent.action.MAIN"
            android:targetPackage="com.android.phone"
            android:targetClass="com.android.phone.MobileNetworkSettings" />
    </header>
.........
</preference-headers>
各選択とクリックできるアイテムは基本的に四つの属性があります.headerを例にとる
id:対応のid
fragment:クリックした後のfragment:WifiSettings
title:headerのメインタイトル、つまりSettingsメインインターフェースに表示されるテキスト:WLAN
icon:headerのアイコン、つまりテキストの左側に表示されているアイコンです.
この2つのファイルを分析してSettingsのレイアウトをまとめることができます.SettingsのメインインターフェースはPreferenceActivityを介して、Preferenceは偏愛偏愛偏愛を意味しています.キーパッドを使って記録ユーザーの前回の選択を記録し、次回このインターフェースに入る時に直接最後の選択を読み取って配置する必要がないのが特徴です.Activityはインターフェースを意味し、preferenceactivityは両者を結合します.各行は一つのheaderに属し、listviewの中のitemに相当し、それぞれのheaderはfragmentに対応しています.fragmentのロードはActivityに依存しています.依存するActivityはSubSettings.javaです.
/**
 *Stub class for showing sub-settings; we can't use the main Settings class
 * since for our app it is a special singleTask class.
 *       Settings.java  fragment,  ,          singleTask
 */
public class SubSettings extends Settings {

    @Override
    public boolean onNavigateUp() {
        finish();
        return true;
    }

    @Override
    protected boolean isValidFragment(String fragmentName) {
        return true;
    }
} 
Settingソースの分析は二つのステップに分けて入手できます.
最初に、headersリストの読み込み
第二に、headerのクリックイベントの処理
以上の二つの問題を解決したら、異なるモジュールの分析を開始できます.
2,インターフェースレイアウトを設定し、headersをロードする.
(1)xmlレイアウトファイルの読み込み
二種類の方法でxmlファイルレイアウトを読み込むことができます.
方法1:
loadHeadersFromResource(R.xml.settings_headers, headers);
方法二:
addPreferencesFromResource(R.xml.fragmented_preferences_inner); 
(2)アドホッターを定義してアドホックをロードして表示する
 private static class HeaderAdapter extends ArrayAdapter<Header> {
インターフェースレイアウトを設定するアダプターadapperには、以下のタイプがあります.
 i>,HEADER_TYPE_CATEGORY:ピントがないので、クリックしてはいけません.
 ii>,HEADER_TYPE_BUTON:buttonを持つheader、buttonのvisibility(視認性)には条件があります.
 iii>,HEADER_TYPE_NORMAL:正常にフォーカスを取得できます.クリックできるのはbuttonを持たないheaderです.
3、Settings.javaソース分析(部分抽出)
(1)onCreateの方法で:
if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
            getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
        }
以上のコードはwindowsのuiをレイアウトするために使われます.つまり上部のステータスバーのレイアウトは、通常は変更されません.
mAuthenticatorHelper = new AuthenticatorHelper();
        mAuthenticatorHelper.updateAuthDescriptions(this);
        mAuthenticatorHelper.onAccountsUpdated(this, null);
このコードは認証を設定したり、口座情報を更新したりします.普通は変更しません.
getMetaData();
表示方法のソースコードを見ることができます:メソッドは、配置ファイルAndroid manifest.xmlのノードのデータを取得することです.
private void getMetaData() {
        try {
            //       Androidmanifest.xml   <meta-data.../>      
           ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
                    PackageManager.GET_META_DATA);
            //      ,   
           if (ai == null || ai.metaData == null) return;
            //   header    id
            mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
           //   header    fragment  
            mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);

            // Check if it has a parent specified and create a Header object
           //       parent,  ,     
            //parent title
           final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
           //parent fragment
           String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);

			if (parentFragmentClass != null) {
                mParentHeader = new Header();
                mParentHeader.fragment = parentFragmentClass;
                if (parentHeaderTitleRes != 0) {
                    mParentHeader.title = getResources().getString(parentHeaderTitleRes);
                }
            }
        } catch (NameNotFoundException nnfe) {
            // No recovery
        }
    }
if (!onIsHidingHeaders() && onIsMultiPane()) {
            highlightHeader(mTopLevelHeaderId);
            // Force the title so that it doesn't get overridden by a direct launch of
            // a specific settings screen.
            setTitle(R.string.settings_label);
        }
onIsMultiPane()はダブルスクリーンMultiPaneかどうかを判断し、フラットパネルのダブルスクリーン表示、携帯電話の一般的なシングルスクリーンのSinglePaneは表示するので、onIsMultiPane()方法はfalseに戻るように設定することができます.
onIsHdingHeadersは、両方のスクリーンのheadersが表示されているかどうかを判断します.
条件が満たされれば、highlight Header()の方法で他のheaderと区別し、ナビゲーションバーtitleを設定にし、上書きしないようにします.
if (onIsMultiPane()) {
           //                   ,false     
            getActionBar().setDisplayHomeAsUpEnabled(false);
            //             ,false      
            getActionBar().setHomeButtonEnabled(false);
             //              
             getActionBar().setDisplayShowHomeEnabled(true)  
}
 
 
 
 以上代码是说如果是多屏显示,则对导航栏左上角程序图标以及返回图标的设置

接下来是利用savedInstanceState恢复数据的操作,不再贴出

showBreadCrumbs(mCurrentHeader.title, null);
現在のheaderのタイトル表示を設定します.
if (mParentHeader != null) {
            setParentTitle(mParentHeader.title, null, new OnClickListener() {
                @Override
                public void onClick(View v) {
                    。。。。。。
                }
            });
        }
parentheaderのタイトルtitleとtitleを設定するクリックイベントを設定します.
 (2)オンスレム方法は、すべてのheaderを示し、header Adapter.reume()方法によって表示される.
headerすなわちitemは、どのようなタイプのレイアウトを表示する必要がありますか?このadapperで変更することができます.異なるアイテムに対して異なるレイアウトファイルを設定します.
private static class HeaderAdapter extends ArrayAdapter<Header> {
static int getHeaderType(Header header) {
     .........
}
 public View getView(int position, View convertView, ViewGroup parent) {
    ..........
}
}
 (3)onBulildStartFrangentIntent方法
@Override
    public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
            int titleRes, int shortTitleRes) {
        Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
                titleRes, shortTitleRes);
		

        // Some fragments want split ActionBar; these should stay in sync with
        // uiOptions for fragments also defined as activities in manifest.
        //  header    fragment         window    
         if (WifiSettings.class.getName().equals(fragmentName) ||
                WifiP2pSettings.class.getName().equals(fragmentName) ||
                BluetoothSettings.class.getName().equals(fragmentName) ||....) {
               //           fragment   activity,    SubSettings
              intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
			
        }
 
        intent.setClass(this, SubSettings.class);
        return intent;
    }
 (4)onBuiildHeaders方法は、レイアウト、およびheadersを更新し、PreferenceActivityのoncreate()方法で呼び出され、さらにonGet Initial Header()方法もPreferenceActivityのoncreate方法で呼び出されます.
@Override
    public void onBuildHeaders(List<Header> headers) {
    
        if (!onIsHidingHeaders()) {
            loadHeadersFromResource(R.xml.settings_headers, headers);
			  
                   //            header    ,
                 //                 header
                     updateHeaderList(headers); } }
 
  
 

(5)doValidCheck(),以及isValidFragment 用来检查fragment是否有效,为适配Android4.4以下版本,保证不出异常

(6)onNewIntent:activity启动模式为singletask单任务模式,如果在战中存在activity的实例,当再次通过intent调起时不会再去oncreate创建实例,而是onNewIntent去重用该实例

 @Override
    public void onNewIntent(Intent intent) {
  
        super.onNewIntent(intent);

        // If it is not launched from history, then reset to top-level
        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
            if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
                switchToHeaderLocal(mFirstHeader);
            }
            getListView().setSelectionFromTop(0, 0);
        }
    }
(7)Settings.javaの中の内部類、Settings.javaの中に多くの実現の内部類があります.
。。。。。。
    public static class SecuritySettingsActivity extends Settings { /* empty */ }
    public static class LocationSettingsActivity extends Settings { /* empty */ }
。。。。。。。。
これらの内部クラスは、fragmentをロードするために、fragmentの宿主として、Android manift.xmlから見ることができます.
<activity android:name="Settings$WirelessSettingsActivity"
                android:taskAffinity="com.android.settings"
                android:label="@string/wireless_networks_settings_title"
                android:parentActivityName="Settings">
。。。。。。
</activity>
4、カスタム操作
Settingsインターフェースのレイアウト原理が分かりましたら、Settingsのメインインターフェースのレイアウトを勝手に添削できます.対応はheaderの修正です.
(1)headerを修正する:xmlファイルの下に修正したいheader対応のノード、テキスト、テキストの左側のアイコンを見つけ、入力したfragmentをクリックして修正すればいいです.
(2)headerを追加します.例えば「権限管理」を追加したいです.作り方は以下の通りです.
    i>、Settings.headers.xmlファイルにheaderノードを追加します.
<header
        android:id="@+id/authority_management
        android:fragment="com.android.settings.AuthorityManagementSettings"
        android:icon="@drawable/ic_settings_authority"
        android:title="@string/authority_settings"/>
     ii>,新規fragment,AuthortyManagement Settings類
public class DeviceInfoSettings extends RestrictedSettingsFragment {
 ........

 @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        addPreferencesFromResource(R.xml.authority_management_settings);
    .........
}
}