Android 6.0 Settings--設置主頁加載流程

Android 6.0 Settings–設置主頁加載流程

聲明

鄭重聲明:博文爲原創內容,可以轉載或引用,但必須在明顯位置標明原文作者和出處,未經同意不得擅自修改本文內容!

博客地址:http://blog.csdn.net/luzhenrong45


代碼環境

以下博文內容基於Android 6.0 Setting原生代碼

Settings

Settings也即大家經常用到的設置應用,每一個Android設備都會預置Settings.apk(MTK平臺重命名爲MtkSettings.apk),一般預置在/system/priv-app/目錄下。Settings 一般是作爲系統全局類功能設置和信息展示的服務類應用,,並對外提供多種功能入口,比如網絡設置、鈴聲設置、日期設置、手機信息顯示等等,部分功能入口支持從第三方應用跳轉進入。這裏從代碼角度記錄下,從啓動設置應用到顯示一級UI頁面的大致加載流程。

首先看下原生設置啓後動的一級主頁樣貌:
settings -note

圖示設置主頁加載流程

先以一張圖概述整個設置主頁的加載流程,下面再通過文字形式對這個過程進行學習記錄。

設置加載主頁流程-qq

文解設置主頁加載流程

設置入口

查看AndroidMenufest.xml找到入口,Settings對應主Activity入口爲Settings

【Settings/AndroidMenufest.xml】

        <!-- Alias for launcher activity only, as this belongs to each profile. -->
        <activity-alias android:name="Settings"
                android:taskAffinity="com.android.settings"
                android:label="@string/settings_label_launcher"
                android:launchMode="singleTask"
                android:targetActivity="Settings">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>

Settings繼承自SettingsActivity, 裏面只是聲明瞭一系列內部靜態類,這些內部靜態類對應着各個功能項的設置入口,除此之外Setting並沒有做其他工作。因此,設置的啓動加載實際是在SettingsActivity裏面實現的。

【Settings/Settings.java】

public class Settings extends SettingsActivity {

    /*
    * Settings subclasses for launching independently.
    */
    public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
    public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ }
    public static class SimSettingsActivity extends SettingsActivity { /* empty */ }
    public static class TetherSettingsActivity extends SettingsActivity { /* empty */ }
    public static class VpnSettingsActivity extends SettingsActivity { /* empty */ }
    public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }
    
    .......
}

設置主頁加載

SettingsActivity

SettingsActivity繼承自Activity,並實現多種接口, 可以說是整個Settings最爲核心的一個類,設置裏面絕大多數的頁面從SettingsActivity衍生或跳轉,包括今天負責主頁頁面加載顯示的DashboardSummary。

【Settings/SettingsActivity.java】

    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
      
        // Should happen before any call to getIntent()
        // 讀取是否有配置好的meta數據,進入主頁這裏返回是null
        getMetaData();

        final Intent intent = getIntent();
       
        // Getting Intent properties can only be done after the super.onCreate(...)
        final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);

        final ComponentName cn = intent.getComponent();
        final String className = cn.getClassName();

        //mIsShowingDashboard這個變量是關鍵,決定是否顯示Dashboard主頁,
        //前面我們說到設置的入口Activity就是Settings.class,因此如果是正常啓動設置,這裏會是true
        mIsShowingDashboard = className.equals(Settings.class.getName());

        setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

        mContent = (ViewGroup) findViewById(R.id.main_content);

        getFragmentManager().addOnBackStackChangedListener(this);

        if (savedState != null) {
            ......
        } else {
            if (!mIsShowingDashboard) {
            
                Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                switchToFragment(initialFragmentName, initialArguments, true, false, mInitialTitleResId, mInitialTitle, false);
            } else {
               //mIsShowingDashboard爲true,因此會跳轉到 DashboardSummary 中
                mInitialTitleResId = R.string.dashboard_title;
                switchToFragment(DashboardSummary.class.getName(), null, false, false, mInitialTitleResId, mInitialTitle, false);
            }
        }
        
       ......
    }

SettingsActivity的onCreate的函數裏面會獲取和初始化很多相關變量,其中我們比較關注的是mIsShowingDashboard這個布爾型變量,從變量名稱不難判斷,這個是代表是否顯示Dashboard主頁的。如果該變量是true,則下面會通過 Fragment 常見的replace方式,跳轉到DashboardSummary中。

【Settings/SettingsActivity.java】

    switchToFragment(DashboardSummary.class.getName(), null, false, false,
                        mInitialTitleResId, mInitialTitle, false);

    /**
     * Switch to a specific Fragment with taking care of validation, Title and BackStack
     */
    private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
            boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
        if (validate && !isValidFragment(fragmentName)) {
            throw new IllegalArgumentException("Invalid fragment for this activity: "
                    + fragmentName);
        }
        Fragment f = Fragment.instantiate(this, fragmentName, args);
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.replace(R.id.main_content, f);
        if (withTransition) {
            TransitionManager.beginDelayedTransition(mContent);
        }
        if (addToBackStack) {
            transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
        }
        if (titleResId > 0) {
            transaction.setBreadCrumbTitle(titleResId);
        } else if (title != null) {
            transaction.setBreadCrumbTitle(title);
        }
        transaction.commitAllowingStateLoss();
        getFragmentManager().executePendingTransactions();
        return f;
    }

另外,與主頁加載相關的,SettingsActivity還爲後續 的DashboardSummary做了dashboard_categories.xml的解析工作:

先看一下dashboard_categories.xml的內容和格式:

【Settings/res/xml/dashboard_categories.xml】

<dashboard-categories
        xmlns:android="http://schemas.android.com/apk/res/android">

    //dashboard-category代表一個大的功能分區
    <!-- WIRELESS and NETWORKS -->
    <dashboard-category
            android:id="@+id/wireless_section"
            android:key="@string/category_key_wireless"
            android:title="@string/header_category_wireless_networks" >

        <!-- Wifi -->
        <dashboard-tile
                android:id="@+id/wifi_settings"
                android:title="@string/wifi_settings_title"
                android:fragment="com.android.settings.wifi.WifiSettings"
                android:icon="@drawable/ic_settings_wireless"
                />
                
        ...... //dashboard-tile代表某個具體功能項,如藍牙,wifi,數據等
        
        <!-- Bluetooth -->
        <dashboard-tile
                android:id="@+id/bluetooth_settings"
                android:title="@string/bluetooth_settings_title"
                android:fragment="com.android.settings.bluetooth.BluetoothSettings"
                android:icon="@drawable/ic_settings_bluetooth"
                />
    </dashboard-category>
    
    ...... //其他分區和子項類同
    
</dashboard-categories>

說明:

  • 一個dashboard-categories裏面包含多個dashboard-category
  • 一個dashboard-category裏面包含多個dashboard-tile
  • 每一個dashboard-category代表一個大的分區,原生設置有四大區,分別爲“無線和網絡”、“設備”、“個人”、“系統”。無線與網絡區裏面會包含藍牙,wifi,數據等多個子項
  • 每一個dashboard-tile代表一個具體的功能項入口,如藍牙、wifi、數據等

dashboard_categories.xml可以理解爲設置主頁的“layout”,只不過它並非我們常見的佈局文件的格式,需要對其進行解析。而提此重任的即是SettingsActivity,SA通過pull的方式對xml文件內容進行解析,將解析的結果保存在類型爲DashboardCategory的List列表中,返回給後面的DashboardSummary調用。

【Settings/SettingsActivity.java】

    public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
        if (forceRefresh || mCategories.size() == 0) {
            buildDashboardCategories(mCategories);
        }
        return mCategories;
    }
    
    private void buildDashboardCategories(List<DashboardCategory> categories) {
        categories.clear();
        //解析dashboard_categories.xml文件
        loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
        updateTilesList(categories);
    }
    
    

xml解析方式是常用的pull方式:

小Q截圖-20200310162029
小Q截圖-20200310161905

DashboardSummary

DashboardSummary主要有以下幾個作用:

  1. 獲取SettingsActivity對dashboard_categories.xml的解析結果
  2. 根據獲取的DashboardCategory列表,依次加載顯示各個列表項,形成設置一級頁面
  3. 註冊Package廣播接收器,監聽Package移除、更新、改變等狀態變化,更新UI頁面

【Settings/DashboardSummary.java】

    //負責更新加載主頁UI
    private void rebuildUI(Context context) {
        ......

        mDashboard.removeAllViews();

        //獲取SettingsActivity對dashboard_categories.xml的解析結果
        List<DashboardCategory> categories =
                ((SettingsActivity) context).getDashboardCategories(true);

        //獲取DashboardCategory個數,原生代碼有四大區
        final int count = categories.size();

        //依次對解析的DashboardCategory進行處理,添加子view
        for (int n = 0; n < count; n++) {
            DashboardCategory category = categories.get(n);

            View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
                    false);

            TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
            //設置分區標題
            categoryLabel.setText(category.getTitle(res));

            ViewGroup categoryContent =
                    (ViewGroup) categoryView.findViewById(R.id.category_content);

            final int tilesCount = category.getTilesCount();
            //設置分區裏面的列表項圖標和入口標題
            for (int i = 0; i < tilesCount; i++) {
                DashboardTile tile = category.getTile(i);

                DashboardTileView tileView = new DashboardTileView(context);
                updateTileView(context, res, tile, tileView.getImageView(),
                        tileView.getTitleTextView(), tileView.getStatusTextView());

                tileView.setTile(tile);

                categoryContent.addView(tileView);
            }

            // Add the category
            //將處理好的DashboardCategory添加到Dashboard主頁中來
            mDashboard.addView(categoryView);
        }
        long delta = System.currentTimeMillis() - start;
        Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");
    }

下面是註冊監聽package的狀態變化,及時更新UI頁面。

【Settings/DashboardSummary.java】

    private class HomePackageReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //監聽狀態變化,更新UI
            rebuildUI(context);
        }
    }
    private HomePackageReceiver mHomePackageReceiver = new HomePackageReceiver();
    
    @Override
    public void onResume() {
        super.onResume();

        sendRebuildUI();

        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        filter.addDataScheme("package");
        getActivity().registerReceiver(mHomePackageReceiver, filter);
    }
    
    

至此,設置的一級頁面就加載顯示出來了。


修改說明

作者 版本 修改時間 修改說明
WalkAloner V1.0 2020/03/11 第一版
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章