【Settings开发】首页加载流程

    Settings即安卓设置应用,用户使用频率较高,可根据其偏好对android系统的各项功能、属性进行个性化配置。其源码位于/packages/apps/Settings目录下。阅读一个应用首先应从其清单文件中入手,找到入口activity。我们进入Setttings的AndroidManifest.xml中:

        <activity android:name="Settings"
                android:taskAffinity="com.android.settings"
                android:label="@string/settings_label_launcher"
                android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.settings.SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                android:value="true" />
        </activity>

        <!-- 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>

    可看到内部的activity-alias标签中有android.intent.action.MAIN(即应用入口action)。此标签较为少见,通过字面意思可理解为'activity别名'。此别名一般是提供给外部环境使用的。

    打个比方,在Launcher中一般都会有数据库持久化存储各个应用的启动intent,intent内部包含有我们的main activity。假设某项目之前的main activity一直是com.why.demo.MainActivity,Launcher也一直是按此名称进行本地存储intent和启动应用。但某个版本我们把入口actiivty名改为了com.why.demo.HomeActivity,当用户升级到此版本时,可能由于Launcher数据库信息未更新,导致我们启动intent还是去启动MainActiivty而造成启动失败。而我们actiivty-alias标签的使用则可避免此问题,我们在activity-alias中通过加入android.intent.category.LAUNCHER和android.intent.action.MAIN两种intent-filter标识此activity-alias标签中的name=value为应用启动类(此name不表示一个实际Actiivty),此name即为外部环境如Launcher中存储的应用启动intent内部所标识的启动activity。当系统根据此name去启动应用时如果发现name所表示的是一个activity-alias。系统会去获取activity-alias的tartgetActivity(此处才是实际启动的Activity)。这就隔离了targetActivity对外部造成影响。

    再回来看我们的Settings项目的清单文件,显然启动类targetActivity即是Settings类。

    进入Settings.java中,发现其为空实现类,其内部只有一些供外部环境独立调用的Activity(这些Activity也是空实现,其真正的逻辑在清单文件中的对应的meta-data的com.android.settings.FRAGMENT_CLASS所声明的fragment中)。Settings所有逻辑都在父类SettingsActivity中。

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 */ }
    public static class StorageSettingsActivity extends SettingsActivity { /* empty */ }
    public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
    public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
    public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ }
    public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }
    public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ }
    public static class VoiceInputSettingsActivity extends SettingsActivity { /* empty */ }
    public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ }
    public static class LocalePickerActivity extends SettingsActivity { /* empty */ }
    public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ }
    public static class HomeSettingsActivity extends SettingsActivity { /* empty */ }
    public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ }
    public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ }
    public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
    public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }
    public static class AppOpsSummaryActivity extends SettingsActivity {
        @Override
        public boolean isValidFragment(String className) {
            if (AppOpsSummary.class.getName().equals(className)) {
                return true;
            }
            return super.isValidFragment(className);
            }
    }
    public static class StorageUseActivity extends SettingsActivity { /* empty */ }
    public static class DevelopmentSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccessibilitySettingsActivity extends SettingsActivity { /* empty */ }
    public static class CaptioningSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccessibilityInversionSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccessibilityContrastSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccessibilityDaltonizerSettingsActivity extends SettingsActivity { /* empty */ }
    public static class SecuritySettingsActivity extends SettingsActivity { /* empty */ }
    public static class UsageAccessSettingsActivity extends SettingsActivity { /* empty */ }
    public static class LocationSettingsActivity extends SettingsActivity { /* empty */ }
    public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ }
    public static class RunningServicesActivity extends SettingsActivity { /* empty */ }
    public static class ManageAccountsSettingsActivity extends SettingsActivity { /* empty */ }
    public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
    public static class BatterySaverSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccountSyncSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccountSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AccountSyncSettingsInAddAccountActivity extends SettingsActivity { /* empty */ }
    public static class CryptKeeperSettingsActivity extends SettingsActivity { /* empty */ }
    public static class DeviceAdminSettingsActivity extends SettingsActivity { /* empty */ }
    public static class DataUsageSummaryActivity extends SettingsActivity { /* empty */ }
    public static class AdvancedWifiSettingsActivity extends SettingsActivity { /* empty */ }
    public static class SavedAccessPointsSettingsActivity extends SettingsActivity { /* empty */ }
    public static class TextToSpeechSettingsActivity extends SettingsActivity { /* empty */ }
    public static class AndroidBeamSettingsActivity extends SettingsActivity { /* empty */ }
    public static class WifiDisplaySettingsActivity extends SettingsActivity { /* empty */ }
    public static class DreamSettingsActivity extends SettingsActivity { /* empty */ }
    public static class NotificationStationActivity extends SettingsActivity { /* empty */ }
    public static class UserSettingsActivity extends SettingsActivity { /* empty */ }
    public static class NotificationAccessSettingsActivity extends SettingsActivity { /* empty */ }
    public static class ConditionProviderSettingsActivity extends SettingsActivity { /* empty */ }
    public static class UsbSettingsActivity extends SettingsActivity { /* empty */ }
    public static class TrustedCredentialsSettingsActivity extends SettingsActivity { /* empty */ }
    public static class PaymentSettingsActivity extends SettingsActivity { /* empty */ }
    public static class PrintSettingsActivity extends SettingsActivity { /* empty */ }
    public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ }
    public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ }
    public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ }
    public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
    public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
    public static class OtherSoundSettingsActivity extends SettingsActivity { /* empty */ }
    public static class QuickLaunchSettingsActivity extends SettingsActivity { /* empty */ }

    public static class TopLevelSettings extends SettingsActivity { /* empty */ }
    public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
}

    进入Settings的父类SettingsActivity中,阅读一个Activity,应从其onCreate方法中开始:

/**
     * Settings入口
     *
     * @param savedState
     */
    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);

        //获取当前Activity的清单文件中的meta-data,
        // 在meta-data中有次activity所要打开的fragment信息,次方法必须在getIntent之前被调用
        getMetaData();
        //getIntent方法已被SettingsActivity重写,内部包装了fragment
        final Intent intent = getIntent();
        if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
            getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
        }
        //sp文件
        mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
                Context.MODE_PRIVATE);

        //取出intent包裹的fragment
        final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);

        mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
        //获取启动此activity的组件class名
        final ComponentName cn = intent.getComponent();
        final String className = cn.getClassName();

        //标识是否是Settings入口打开的此activity
        mIsShowingDashboard = className.equals(Settings.class.getName());

        //标识是否是子Settings类。
        final boolean isSubSettings = className.equals(SubSettings.class.getName()) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);

        // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
        if (isSubSettings) {
            // Check also that we are not a Theme Dialog as we don't want to override them
            final int themeResId = getThemeResId();
            if (themeResId != R.style.Theme_DialogWhenLarge &&
                    themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
                setTheme(R.style.Theme_SubSettings);
            }
        }
        //settings_main_dashboard是FrameLayout - id->main_content之后会被fragment替换
        setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
        //mContent被fragment replace掉
        mContent = (ViewGroup) findViewById(R.id.main_content);
        //此处是当stack发生变化时
        getFragmentManager().addOnBackStackChangedListener(this);

        if (mIsShowingDashboard) {
            Index.getInstance(getApplicationContext()).update();
        }
        //变量与集合初始化
        if (savedState != null) {
            // We are restarting from a previous saved state; used that to initialize, instead
            // of starting fresh.
            mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
            mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
            //此处和OnBackStackChanged异曲同工。
            setTitleFromIntent(intent);

            ArrayList<DashboardCategory> categories =
                    savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
            if (categories != null) {
                mCategories.clear();
                mCategories.addAll(categories);
                setTitleFromBackStack();
            }

            mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
            mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
            mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
                    1 /* one home activity by default */);
        } else {
            //saveState为空,即初次进来。
            if (!mIsShowingDashboard) {//表示非Settings页面(入口页面)
                // Search is shown we are launched thru a Settings "shortcut". UP will be shown
                // only if it is a sub settings
                if (mIsShortcut) {//快捷方式
                    mDisplayHomeAsUpEnabled = isSubSettings;
                    mDisplaySearch = false;
                } else if (isSubSettings) {//如果是子页面
                    mDisplayHomeAsUpEnabled = true;
                    mDisplaySearch = true;
                } else {
                    mDisplayHomeAsUpEnabled = false;
                    mDisplaySearch = false;
                }
                //设置title
                setTitleFromIntent(intent);

                Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                //main_content替换为目标fragment
                switchToFragment(initialFragmentName, initialArguments, true, false,
                        mInitialTitleResId, mInitialTitle, false);
            } else {//表示当前实在Settings入口
                // No UP affordance if we are displaying the main Dashboard
                mDisplayHomeAsUpEnabled = false;//返回键
                // Show Search affordance
                mDisplaySearch = true;//query图标
                mInitialTitleResId = R.string.dashboard_title;//Settings
                //
                switchToFragment(DashboardSummary.class.getName(), null, false, false,
                        mInitialTitleResId, mInitialTitle, false);
            }
        }

        mActionBar = getActionBar();
        if (mActionBar != null) {
            mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
            mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
        }
        mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);

        // see if we should show Back/Next buttons
        if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {

            View buttonBar = findViewById(R.id.button_bar);
            if (buttonBar != null) {
                buttonBar.setVisibility(View.VISIBLE);

                Button backButton = (Button)findViewById(R.id.back_button);
                backButton.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        setResult(RESULT_CANCELED, getResultIntentData());
                        finish();
                    }
                });
                Button skipButton = (Button)findViewById(R.id.skip_button);
                skipButton.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        setResult(RESULT_OK, getResultIntentData());
                        finish();
                    }
                });
                mNextButton = (Button)findViewById(R.id.next_button);
                mNextButton.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        setResult(RESULT_OK, getResultIntentData());
                        finish();
                    }
                });

                // set our various button parameters
                if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
                    String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
                    if (TextUtils.isEmpty(buttonText)) {
                        mNextButton.setVisibility(View.GONE);
                    }
                    else {
                        mNextButton.setText(buttonText);
                    }
                }
                if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
                    String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
                    if (TextUtils.isEmpty(buttonText)) {
                        backButton.setVisibility(View.GONE);
                    }
                    else {
                        backButton.setText(buttonText);
                    }
                }
                if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
                    skipButton.setVisibility(View.VISIBLE);
                }
            }
        }

        mHomeActivitiesCount = getHomeActivitiesCount();
    }

    上边流程分析:

        1.getMetaData(),此方法是从清单文件中获取Activity的meta-data元信息。在各启动Activity中大都在meta-data中定义了此Activity所对应的fragment,getMetaData方法取出元信息中fragment并保存在mFragmentClass成员变量中。

private void getMetaData() {
        try {
            ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
                    PackageManager.GET_META_DATA);
            if (ai == null || ai.metaData == null) return;
            mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
        } catch (NameNotFoundException nnfe) {
            // No recovery
            Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
        }
    }

    2.getIntent(),此方法已被SettingsActivity重写,用于处理第一步获取的fragment,设置回intent中。可通过EXTRA_SHOW_FRAGMENT获取intent中保存的fragment。

public Intent getIntent() {
        Intent superIntent = super.getIntent();
        //获取当前activity要打开的fragment,内部会先从清单文件的meta-data中获取
        String startingFragment = getStartingFragmentClass(superIntent);
        // This is called from super.onCreate, isMultiPane() is not yet reliable
        // Do not use onIsHidingHeaders either, which relies itself on this method
        if (startingFragment != null) {//代表有需要打开的fragment
            Intent modIntent = new Intent(superIntent);
            //把需要打开的fragment作为extra放入intent中。
            modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
            //fragment是否有提供预置参数
            Bundle args = superIntent.getExtras();
            if (args != null) {
                args = new Bundle(args);
            } else {
                args = new Bundle();
            }
            args.putParcelable("intent", superIntent);
            modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
            //返回包裹后的intent
            return modIntent;
        }
        return superIntent;
    }

    3.取出步骤2中保存在intent中的fragment。并根据启动Activity是否为Settings判断当前是否是dashboard界面(首页)。根据此判断设置不同的contentView,本篇文章我们仅分析首页Settings加载过程,由此可知,当前设置的layout为R.layout.settings_main_dashboard文件,此布局中仅有一个framelayout作为fragment占位容器。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/main_content"
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             android:background="@color/dashboard_background_color"
             />

    4.设置fragment回退栈回退监听。在监听listener中如果发现当前有fragment被back掉,则会改变actionbar的title。各个fragment的title时存放在BackStackEntry内部的breadCrumbTitle中。

    5.接下来主要是进行saveState判断是否需Activity恢复现场。当我们首次进入时进入的是else流程,在此流程中是否是Settings页面(首页)来给main_content布局填充不同的fragment。而本文讨论的首页加载则可从代码中看出,其是填充了DashboardSummary类(其实质是一个Fragment)。当然此处也对一些actionbar的控件显示作为不同区分。

    上文首页Setttings页面会加载DashboardSummary到main_content上显示,我们进入DashboardSummary中,发现其继承自fragment,在onCreateView中填充了一个线性布局。当DashboardSummary执行onResume时会去rebuildUI方法,在此方法中完成对UI页面的构建。同时还监听包变化(PACKAGE_ADDED,ACKAGE_REMOVED,PACKAGE_CAHNGED,PACKAGE_REPLACED),在包变化时也会执行rebuildUI操作。

private void rebuildUI(Context context) {
        //判断fragment是否已经被add
        if (!isAdded()) {
            Log.w(LOG_TAG, "Cannot build the DashboardSummary UI yet as the Fragment is not added");
            return;
        }

        long start = System.currentTimeMillis();
        final Resources res = getResources();
        //先清空掉所有的行
        mDashboard.removeAllViews();
        //此处调用SettingsActivity中的方法去获取所有的行
        List<DashboardCategory> categories =
                ((SettingsActivity) context).getDashboardCategories(true);

        final int count = categories.size();

        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
            mDashboard.addView(categoryView);
        }
        long delta = System.currentTimeMillis() - start;
        Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");
    }

    在rebuildUI中,线性布局mDashboard清空所有子控件后,会调用SettingsActivity(即此fragment依存类)的getDashboardCategories方法从xml中解析列表。把解析出来的DashboardCategory和DashboardTile(两者实质上为model对象,数据实体)对应到DashboardContainerView(容器控件,内部layout方式类似于线性布局)和DashboardTileView中去,在UI层级上来说,DashboardContainerView作为mDashboard线性布局子控件存在,DashboardTileView作为DashboardContainerView子控件存在。

    进入到getDashboardCategories中根据参数和mCategories集合的size来判断是否需要从xml中解析并重构数据。

    public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
        if (forceRefresh || mCategories.size() == 0) {
            buildDashboardCategories(mCategories);
        }
        return mCategories;
    }
    /**
     * Called when the activity needs its list of categories/tiles built.
     *
     * @param categories The list in which to place the tiles categories.
     */
    private void buildDashboardCategories(List<DashboardCategory> categories) {
        categories.clear();//首先清理集合,
        loadCategoriesFromResource(R.xml.dashboard_categories, categories);//加载xml文件
        updateTilesList(categories);//此处会去除一些不可用的设置项
    }

    可以看到loadCategoriesFromResource方法会去解析res/xml文件夹中的dashboard_categories文件,在此文件中即是首页显示的列表数据。此文件内部区分为四个大类(dashboard-category),各大类下面又分为多条数据(dashboard-tile)。

        1.Wireless&Networks。无线和网络设置部分,其内部有WiFi、蓝牙、移动网络、其他等列。

        2.Device。设备设置部分,其内部分为Home、显示、声音&通知、存储、电池管理、应用管理、用户管理、NFC等列。

        3.Personal。个性设置部分,其内部分为位置、安全、账户、语言和输入法、备份和还原等列。

        4.System。系统设置部分,其内部分为日期和时间、辅助功能、打印、开发者选项、关于手机等列。

    当然,dashboard_categories中这些列也并非全部显示到了设置列表中,buidlDashboardCategories方法在加载完xml文件后,会去根据各个列表功能的可用性保留加载的数据,即updateTileList方法。

/**
     * 更新category集合中的内容,主要根据SystemFeature来看当前title关联的设置是否可用
     * @param target
     */
    private void updateTilesList(List<DashboardCategory> target) {
        final boolean showDev = mDevelopmentPreferences.getBoolean(
                DevelopmentSettings.PREF_SHOW,
                android.os.Build.TYPE.equals("eng"));//eng模式adb有root权限,适合开发模式,而user模式下adb只有shell权限

        final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);

        final int size = target.size();
        for (int i = 0; i < size; i++) {

            DashboardCategory category = target.get(i);

            // Ids are integers, so downcasting is ok
            int id = (int) category.id;
            int n = category.getTilesCount() - 1;
            while (n >= 0) {

                DashboardTile tile = category.getTile(n);
                boolean removeTile = false;
                id = (int) tile.id;
                if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
                    if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
                        removeTile = true;
                    }
                } else if (id == R.id.wifi_settings) {
                    //WiFi可用性
                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
                        removeTile = true;
                    }
                } else if (id == R.id.bluetooth_settings) {
                    //蓝牙可用性
                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
                        removeTile = true;
                    }
                } else if (id == R.id.data_usage_settings) {
                    //移动数据管理?
                    final INetworkManagementService netManager = INetworkManagementService.Stub
                            .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
                    try {
                        if (!netManager.isBandwidthControlEnabled()) {
                            removeTile = true;
                        }
                    } catch (RemoteException e) {
                        // ignored
                    }

                } else if (id == R.id.battery_settings) {//电池设置可用性
                    // Remove battery settings when battery is not available. (e.g. TV)

                    if (!mBatteryPresent) {
                        removeTile = true;
                    }
                } else if (id == R.id.home_settings) {
                    if (!updateHomeSettingTiles(tile)) {
                        removeTile = true;
                    }
                } else if (id == R.id.user_settings) {
                    boolean hasMultipleUsers =
                            ((UserManager) getSystemService(Context.USER_SERVICE))
                                    .getUserCount() > 1;
                    if (!UserHandle.MU_ENABLED
                            || (!UserManager.supportsMultipleUsers()
                                    && !hasMultipleUsers)
                            || Utils.isMonkeyRunning()) {
                        removeTile = true;
                    }
                } else if (id == R.id.nfc_payment_settings) {//当前手机是否有nfc支付功能
                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
                        removeTile = true;
                    } else {
                        // Only show if NFC is on and we have the HCE feature
                        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
                        if (adapter == null || !adapter.isEnabled() ||
                                !getPackageManager().hasSystemFeature(//卡仿真技术,模拟nfc卡支付
                                        PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
                            removeTile = true;
                        }
                    }
                } else if (id == R.id.print_settings) {
                    boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
                            PackageManager.FEATURE_PRINTING);
                    if (!hasPrintingSupport) {
                        removeTile = true;
                    }
                } else if (id == R.id.development_settings) {//开发者选项模式
                    //showDev 当前是否处于eng
                    if (!showDev || um.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES)) {
                        removeTile = true;
                    }
                }

                if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
                        && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
                    removeTile = true;
                }

                if (removeTile && n < category.getTilesCount()) {
                    category.removeTile(n);
                }
                n--;
            }
        }
    }

    从updateTileList中可以看见对不可用的功能,会执行remove操作。这些步骤都执行完了之后,就会回到DashboardSummary中,把这些数据一一对应到UI中显示。

    至此,设置页面首页的加载流程就算结束了。

        


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值