Android6.0Settings分析

Android  源码分析  2018年8月3日 pm4:36发布6年前 (2018)更新 城堡大人
131 0 0


源码:Android 6.0

应用:Settings

目录:android-6.0.0_r1\packages\apps\Settings

 

Android 中Settings的代码很多,我这里只是简单分析一下,仅供参考。如果要深入了解设置,自己要多看看源码。

一般分析应用的话会先看其AndroidManifest.xml文件,这里会展示四大组件,默认启动入口Activity等信息。

好了,原归正传,我们开始Settings源码分析流程。

1、AndroidManifest.xml

撇开其他的,我们先找到Settings的入口函数的配置(看红色字体)

        <activity
            android:name="Settings"
            android:label="@string/settings_label_launcher"
            android:launchMode="singleTask"
            android:taskAffinity="com.android.settings" >

            ......
        </activity>

        <!-- Alias for launcher activity only, as this belongs to each profile. -->
        <activity-alias
            android:name="Settings"
            android:label="@string/settings_label_launcher"
            android:launchMode="singleTask"
            android:targetActivity="Settings"
            android:taskAffinity="com.android.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的讲解可以看我摘抄的文章《[摘]activity-alias详解及应用》,这里就不再细说。

恩恩,我们知道入口的Activity是“Settings”

2、Settings

public class Settings extends SettingsActivity {
    public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
    public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ }
    public static class SimSettingsActivity extends SettingsActivity { /* empty */ }
    ......
}

Settings继承于SettingsActivity ,而且其内部都是类的空实现。

因此我们看其父类SettingsActivity

3、SettingsActivity

SettingsActivity是实现于Activity,并且核心功能都在这个类中。

public class SettingsActivity extends Activity{
  ......
}

既然是继承于Activity,那么我们就看其的生命周期流程。

3.1、SettingsActivity.onCreate()

PS:可以看英文注释,有些解释很清楚的

    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);

        // Should happen before any call to getIntent()
        【获取AndroidManiFest.xml中mate数据,从Settings(或桌面快捷图标)进入时获取是null】
        getMetaData();

        final Intent intent = getIntent();
        if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
            getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
        }

        mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
                Context.MODE_PRIVATE);

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


        mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
        【获取intent中的包名和类名】
        final ComponentName cn = intent.getComponent();
        final String className = cn.getClassName();

        【从Settings从快捷键点击进入返回true】
        mIsShowingDashboard = className.equals(Settings.class.getName());

        // This is a "Sub Settings" when:
        // - this is a real SubSettings
        // - or :settings:show_fragment_as_subsetting is passed to the Intent
        final boolean isSubSettings = this instanceof SubSettings ||
                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);
            }
        }

        【mIsShowingDashboard 是 ture】
        setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

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

        【设置Fragment添加监听(onBackStackChanged)】
        getFragmentManager().addOnBackStackChangedListener(this);

        if (mIsShowingDashboard) {
            // Run the Index update only if we have some space
            if (!Utils.isLowStorage(this)) {【判断是否是低内存】
                Index.getInstance(getApplicationContext()).update();
            } else {
                Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
            }
        }

        【第一次进入savedState为null的】
        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);

            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 {【第一次进来走这里,mIsShowingDashboard为true】
            if (!mIsShowingDashboard) {
                mDisplaySearch = false;
                // UP will be shown only if it is a sub settings
                if (mIsShortcut) {
                    mDisplayHomeAsUpEnabled = isSubSettings;
                } else if (isSubSettings) {
                    mDisplayHomeAsUpEnabled = true;
                } else {
                    mDisplayHomeAsUpEnabled = false;
                }
                setTitleFromIntent(intent);

                Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                switchToFragment(initialFragmentName, initialArguments, true, false,
                        mInitialTitleResId, mInitialTitle, false);
            } else {
                // No UP affordance if we are displaying the main Dashboard
                mDisplayHomeAsUpEnabled = false;
                // Show Search affordance
                mDisplaySearch = true;
                mInitialTitleResId = R.string.dashboard_title;
                【3.2 switchToFragment就是切换到DashboardSummary】
                switchToFragment(DashboardSummary.class.getName(), null, false, false,
                        mInitialTitleResId, mInitialTitle, false);
            }
        }

        ......
    }
3.2 switchToFragment

这里是更具传入参数切换对应的Fragment,上面我们传入的是DashboardSummary

    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;
    }

4、DashboardSummary

DashboardSummary也就是一个Fragment,因此我们看看其的生命周期的几个方法。

public class DashboardSummary extends InstrumentedFragment {
    ......
}

public abstract class InstrumentedFragment extends PreferenceFragment {
    ......
}

public abstract class PreferenceFragment extends Fragment implements
        PreferenceManager.OnPreferenceTreeClickListener {
}
4.1、DashboardSummary.onCreateView()
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        mLayoutInflater = inflater;
        【加载一个布局dashboard】
        final View rootView = inflater.inflate(R.layout.dashboard, container, false);
        mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
        return rootView;
    }
4.2、DashboardSummary.onCreateView()
    @Override
    public void onResume() {
        super.onResume();
        【4.3 重新更新UI布局】
        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);
    }
4.3、重新更新UI布局(DashboardSummar.sendRebuildUI())

通知更新UI,重点是调用rebuildUI()方法

    private void sendRebuildUI() {
        if (!mHandler.hasMessages(MSG_REBUILD_UI)) {
            mHandler.sendEmptyMessage(MSG_REBUILD_UI);
        }
    }

    private static final int MSG_REBUILD_UI = 1;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REBUILD_UI: {
                    final Context context = getActivity();
                    rebuildUI(context);
                } break;
            }
        }
    };
4.4、DashboardSummar.rebuildUI()
    private void rebuildUI(Context context) {
        ......
        long start = System.currentTimeMillis();
        final Resources res = getResources();

        mDashboard.removeAllViews();
        【解析xml中的item值,具体看SettingsActivity.getDashboardCategories(boolean)方法】
        List<DashboardCategory> categories =
                ((SettingsActivity) context).getDashboardCategories(true);

        //后面是对解析的数据进行更新UI界面,具体就不看了
        ......
    }

5、SettingsActivity.getDashboardCategories()

    public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
        if (forceRefresh || mCategories.size() == 0) {
            buildDashboardCategories(mCategories);
        }
        return mCategories;
    }

    private void buildDashboardCategories(List<DashboardCategory> categories) {
        【先清空】
        categories.clear();
        【loadCategoriesFromResource加载dashboard_categories中的数据】
        loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
        【6、更新categories到界面上】
        updateTilesList(categories);
    }

至于loadCategoriesFromResource具体我这就不展示了,代码很长,有兴趣的自己去看。

dashboard_categories.xml这里有所有设置选项的配置

6、SettingsActivity.updateTilesList()

这里可以设置设置中某个categories(比如wifi、蓝牙设置)是否展示。

    private void updateTilesList(List<DashboardCategory> target) {
        final boolean showDev = mDevelopmentPreferences.getBoolean(
                DevelopmentSettings.PREF_SHOW,
                android.os.Build.TYPE.equals("eng"));

        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) {
                    // Remove WiFi Settings if WiFi service is not available.
                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
                        removeTile = true;
                    }
                } else if (id == R.id.bluetooth_settings) {
                    // Remove Bluetooth Settings if Bluetooth service is not available.
                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
                        removeTile = true;
                    }
                } else if (id == R.id.data_usage_settings) {
                    // Remove data usage when kernel module not enabled
                    if (!Utils.isBandwidthControlEnabled()) {
                        removeTile = true;
                    }
                } 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()
                            || Utils.isMonkeyRunning()) {
                        removeTile = true;
                    }
                } else if (id == R.id.nfc_payment_settings) {
                    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(
                                        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) {
                    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--;
            }
        }
        addExternalTiles(target);
    }

好了,到这里就基本展示出设置的界面了。

本文参考过《Android6.0源码分析之Settings(一)

 历史上的今天

  1. 2023: rc文件规则简介(0条评论)
  2. 2021: 王单单:在江边喝酒(0条评论)
  3. 2019: Android中实现延时执行操作的几种方法(0条评论)
  4. 2019: 蒋勋:生活的美就在于从容不迫(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

startActivity源码分析1

前言简单记录一下startActivity()源码的启动过程,主要是过一下流程,细节太多顾不上。下面以Launcher界面启动(startActivity())未启动过的应用为例。这部分主要涉及,启动新的Activity前,系统会让当前Activity[叫Launcher吧]进入onPaus...

Android应用启动时出现白屏或者黑屏问题的简介

前言Android系统上,当启动应用时存在闪白屏或者黑屏,这种问题很常见。产生原因:当系统启动一个APP时,zygote进程会首先创建一个新的进程去运行这个APP,但是进程的创建是需要时间的,在创建完成之前,界面是呈现假死状态的,因为用户会以为没有点到APP而再次点击,这极大的降低用户体验,...

世界,您好!

1、好记性不如烂笔头;2、觉得已经晚了,恰恰是最早的时候;3、鸡蛋从外打破是食物,从内打破是生命我们新版上线啦(掌声),这次改版很大,放弃之前传统的导航模式,首先采用“网址导航+读书(阅读)+博客”全新的模式,依旧是国内第一个以读书(阅读)为主题的网址导航大全!因为热爱,所以折腾!P...

sendBroadcast静态广播源码分析

前言本次分析一下Android P开机后发送静态广播源码分析,记录一下,方便自己查阅。部分流程跟前面的源码分析《startActivity源码分析》和《startService源码分析》都比较类似,所以重复的步骤就省略。PS:启动startActivity的比较复杂,流程多,但搞懂了这个,...

李娟: 通往一家人去的路

有时候我会扔下杂货店跑出去满山遍野地玩,来店里买东西的人就只好坐在我家帐篷里耐心等待,顺便替我守着店,有人来买东西的话,就告诉他:“人不在。”有时候他实在等急了,就出去满山遍野地找我。而有的时候呢,我在帐篷里耗一整天,也没有一个人来买东西,连把头伸进帐篷看一眼的人也没有。害我白白浪费了本该出去玩的...

[摘]Android稳定性(一)SWT和ANR

前言查找SWT啥意思时找到这篇文章,加上项目中用的也是MTK平台,因此摘抄这篇文章于此,方便自己学习和查阅。本文摘抄,感谢作者分析。好记性不如烂笔头正文SWT这里解释了什么是SWT以及SWT的作用。什么是SWTSWT:software watchdog,监控SystemServer进...