關於Android Settings中的八個問題


本問將回答以下八個問題,如有錯誤,敬請批評指正,不勝感激!(注:本文中的Settings解析基於android4.0+)

問題一Settings的主界面是怎么實現的?

問題二為什么使用hierarchyviewer Settings中的很多界面顯示的都是SubSettings

問題三hierarchyviewer 中顯示SubSetting時如何確定我進入的是哪個fragment

問題四、點擊設置界面的某一個header時,設置界面是如何切換的?

問題五Settings.javagetMetaDatagetStartingFragmentClass這兩個函數是否有點矛盾?

問題六Settingsshortcut是如何創建的?從shortcut進入Settings的流程是什么?

問題七、為什么我從Settingsshortcut進入時,hierarchyviewer顯示的就不是SubSettings(Data usage)

問題八Settings.java中很多繼承自它的內部類都是空實現,為什么要寫這些類?

-----------------------------------------------------------------------------------------------------------------------------------------

由於項目需要,本人就對Android中的Settings進行了解析,希望能幫到對Settings有興趣的同志們~

-----------------------------------------------------------------------------------------------------------------------------------------

問題一、Settings的主界面是怎么實現的?

為了能適應平板和手機,Settings采用了PreferenceActivityPreferenceFragment結合的實現方式。

Settings.java繼承自PreferenceActivity,是Settings的主界面,它通過loadHeadersFromResource函數(api level 11)加載res/xml/settings_headers.xml來構造界面。在settings_headers.xml中聲明了要在Settings主界面顯示的各個header(如SoundDisplay等)。Settings.HeaderAdapter將其中的header分為三類。在Settings.HeaderAdapter中的getView方法中根據header的類型使用不同的布局文件。

header划分類型的函數

        static int getHeaderType(Header header) {

            if (header.fragment == null && header.intent == null) {

                return HEADER_TYPE_CATEGORY; // 因為沒有指明fragment和intent

            } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings || header.id == R.id.mobiledata_settings) {

                return HEADER_TYPE_SWITCH; // 針對特定的三個header,分別為Wi-Fi、Bluetooth和Mobile data

            } else {

                return HEADER_TYPE_NORMAL;

            }

        }

當我們點擊主界面的header后會顯示與該header相關的設置界面。大部分(Display的詳細設置界面)都是通過繼承PreferenceFragment來實現的;有一部分是在settings_headers.xml中聲明<intent>,當被點擊時(觸發PreferenceActivityonHeaderClick())將會通過startActivity來啟動在<intent>節點中聲明的targetClass(如設置中的Add account)

問題二、為什么使用hierarchyviewer Settings中的很多界面顯示的都是SubSettings

要解決這個問題我們先要清楚為什么會寫一個SubSettings.java繼承自Settings.java

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在聲明時指定了android:launchMode="singleTask"

我們知道,要顯示Fragment的內容,我們就需要為其指定一個Activity。而Settings中的很多設置界面是由PreferenceFragment來完成的,當然也需要我們指定Activity.

onBuildStartFragmentIntent函數會為我們構造一個顯示FragmentIntent對象(該函數的注釋寫的非常明白).Settings.java重寫了這個函數(注,重寫時它調用了super的該方法),在為intent對象setClass時都使用SubSettings.java.(注:在settings_headers.xml指定了intentheader是不會觸發onBuildStartFragmentIntent)

結果就是,Settings中大部分fragment都是使用的SubSettings這個Activity來顯示。由於hierarchyviewer只是顯示當前界面使用的Activity(不能顯示這個界面是由哪個Fragment構造的),所以我們使用hierarchyviewer Settings進行觀察時很多設置界面顯示的是SubSettings

問題三、hierarchyviewer 中顯示SubSetting時如何確定我進入的是哪個fragment

res/xml/settings_headers.xml中聲明了各個header被點擊后使用的fragment。我們可以根據這個文件確定我們進入的fragment

例如,當我們點擊Displayhierarchyviewer 中顯示SubSetting。我們通過查找settings_headers就可知道使用的是哪個fragment

Display這個headersettings_headers.xml中的聲明:

    <!-- Display -->

    <header

        android:id="@+id/display_settings"

        android:icon="@drawable/ic_settings_display"

        android:fragment="com.android.settings.DisplaySettings"

        android:title="@string/display_settings" />

 header中使用 android:fragment指明使用的fragment。由此可知,Display使用的是com.android.settings.DisplaySettings這個fragment

問題四、點擊設置界面的某一個header時,設置界面是如何切換的? 

點擊設置界面的header,會觸發SettingsonHeaderClick函數,主要的處理都在其父類PreferenceActivityonHeaderClick中實現的。如果這個header指定了fragment,在mSinglePane(標識是否為小屏幕設備。如,區分手機還是平板)true時,會調用startWithFragment方法,在startWithFragment方法中將調用onBuildStartFragmentIntent方法來構造intent對象(重要),最后使用該intent對象啟動一個activity來顯示fragment

以點擊Settings中的Display為例.

(Bluetooth同理,只不過啟動的Activity變為BluetoothSettingsActivity(繼承自Settings,但是沒有實現重寫任何方法,所以與SubSettings是一樣的處理),fragment變為 com.android.settings.bluetooth.BluetoothSettings)

fragmentcom.android.settings.DisplaySettingsactivitycom.android.settings.SubSettings.fragment是由onHeaderClick函數傳入的,activity是由onBuildStartFragmentIntent()指定的)

執行startActivity后將啟動SubSettings.java。即我們將會再一次執行SubSettingsPreferenceActivityonCreate方法(因為Settings.javaonCreate方法調用了super.onCreate()),但是這次並不會進入Settings的主界面,因為我們的使用的intent對象是有很大不同的。這一次onCreate函數(PreferenceActivity)中的initialFragment 將被初始化為com.android.settings.DisplaySettings然后我們將進入switchToHeader(),最后switchToHeaderInner會取得FragmentTransaction對象,然后執行了transaction.replace(com.android.internal.R.id.prefs, f).就這樣把我們的fragment顯示出來了。在onCreate中會對其他viewvisibility進行設置,以保證只顯示prefs。如,com.android.internal.R.id.headersvisibility設置為VIEW.GONE.

PreferenceActivity的布局文件為preference_list_content.xml

問題五、Settings.javagetMetaDatagetStartingFragmentClass這兩個函數是否有點矛盾?

這兩個函數可以說是相輔相成的(好官方,^_^)。getMetaData會從AndroidManifest.xml中讀取Activity<meta-data>節點的數據;getStartingFragmentClass則從啟動Activityintent中讀取數據。這兩個函數會對讀取到的數據進行整合,getStartingFragmentClass依賴於getMetaData讀取到的數據,但是它也可能對數據作出修改(為了兼容性,如對原有manage apps類進行特殊處理)

問題六、Settingsshortcut是如何創建的?從shortcut進入Settings的流程是什么?

創建SettingsshortcutLuancher將會啟動CreateShortcut,創建shortcut所需的intent對象將會由CreateShortcut和其父類LuancherActivity共同構建(詳見 CreateShortcutonListItemClick),這時創建的Intent對象使用的就不是SubSettings(LuancherActivityintentForPosition函數執行setClassName()時使用的參數並不是SubSettings).

CreateShortcut中列出了可以創建shortcut的設置項,這些設置項怎樣檢索出來的?

原來,在創建LuancherActivityActivityAdapter對象時,其構造函數中執行了makeListItems函數,該函數將使用PackageManagerqueryIntentActivities來根據intent對象查詢符合條件的activity。使用的intent是從getTargetIntent函數返回的。不難發現,要想在CreateShortcut中顯示,Activity在必須要有

<category android:name="com.android.settings.SHORTCUT" />

如果我們想將Security設置項添加到shortcut列表,我們只需要在androidmanifest.xml中聲明Settings$SecuritySettingsActivity部分加上

<category android:name="com.android.settings.SHORTCUT" />

即可。

回到正題,點擊shortcut進入Settings,傳入Intent對象中包含了目標fragment和目標activity以及其他信息。PreferenceActivity得到了足夠多的信息,因此在onCreate中將依次執行switchToHeader()->setSelectedHeader(null)->switchToHeaderInner()->transaction.replace(com.android.internal.R.id.prefs, f);

這樣就完成了fragment的顯示(使用的activity是從intent解析出來的.switchToHeaderInner中執行Fragment.instantiate時使用的Contextthis!!)像執行onHeaderClick那樣會執行函數onBuildStartFragmentIntent(Settings中重寫了該函數)重新指定我們使用的Activity

問題七、為什么我從Settingsshortcut進入時,hierarchyviewer顯示的就不是SubSettings(Data usage)

hierarchyviewer中顯示SubSettings是因為我們在onBuildStartFragmentIntent方法中做了特殊處理(詳見問題二)。從shortcut進入Settings時不顯示SubSettings是因為沒有走這個函數,因此就不會顯示為SubSettings(詳見問題六)

問題八、Settings.java中很多繼承自它的內部類都是空實現,為什么要寫這些類?

我們從這些內部類的名字可以看出它們的主要作用,如BluetoothSettingsActivity是針對藍牙設置的。空實現說明他們都將使用Settings.java中的函數(注意private的屬性和方法的訪問權限問題)。聲明這些Activity的原因我認為主要是為了提高各個設置項、整個Settings的靈活性,方便開發者進行擴展。除此之外的一些原因:可以讓我們為單獨的設置項添加 shortcut(data usage),因為創建shortcut使用queryIntentActivities查詢使用的activity;允許其它程序訪問單獨的設置項;結構設計需要,啟動Activity會讀取meta-data信息;某些設置項不想使用SubSettings的屬性。如,Settings中點擊Bluetooth時使用BluetoothSettingsActivity,啟動Bluetooth時將使用BluetoothSettingsActivity的屬性,如 android:clearTaskOnLaunch="true"

寫的不全,望補充~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM