启动流程

系统设置作为系统应用一个需要高度客制化的原生应用。以Mtk平台Android S版本的MtkSettings源码为例分析其主要的加载流程

<!-- Alias for launcher activity only, as this belongs to each profile. -->
<!-- [android]singleTask启动模式启动,若存在实例结束掉该实例之上的所有Activity,并将该实例置为栈顶 -->
<activity-alias android:name="Settings"
                android:label="@string/settings_label_launcher"
                android:taskAffinity="com.android.settings.root"
                android:launchMode="singleTask"
                android:exported="true"
                android:targetActivity=".homepage.SettingsHomepageActivity">
            <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>
            <meta-data android:name="android.app.shortcuts"
            	android:resource="@xml/shortcuts"/>
</activity-alias>
/**
- Top-level Settings activity
*/
public class Settings extends SettingsActivity {
   /*
   * Settings subclasses for launching independently.
   */
   public static class AssistGestureSettingsActivity extends SettingsActivity { /* empty */}
   public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
   public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
 ResumedActivity: ActivityRecord{e80f8b1 u0 com.android.settings/.Settings$WifiSettingsActivity t10}
/** Settings homepage activity */
/** [android]主Activity入口,实现LifecycleObserver子类接口CategoryMixin,感知生命周期的变化 */
public class SettingsHomepageActivity extends FragmentActivity implements
        CategoryMixin.CategoryHandler {
    private CategoryMixin mCategoryMixin;

    @Override
    public CategoryMixin getCategoryMixin() {
        return mCategoryMixin;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
		//[android]settings_homepage_container布局内部包含一个支持嵌套的NestedScrollView
        setContentView(R.layout.settings_homepage_container);

        final View appBar = findViewById(R.id.app_bar_container);
        appBar.setMinimumHeight(getSearchBoxHeight());
        initHomepageContainer();

        final Toolbar toolbar = findViewById(R.id.search_action_bar);
        FeatureFactory.getFactory(this).getSearchFeatureProvider()
                .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);

        getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
        mCategoryMixin = new CategoryMixin(this);
        getLifecycle().addObserver(mCategoryMixin);
		...省略
		//[android]显示主界面Fragment:TopLevelSettings
        showFragment(new TopLevelSettings(), R.id.main_content);
        ((FrameLayout) findViewById(R.id.main_content))
                .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
    }
<PreferenceScreen
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:settings="http://schemas.android.com/apk/res-auto"
   android:key="top_level_settings">

   <Preference
       android:fragment="com.android.settings.network.NetworkDashboardFragment"
       android:icon="@drawable/ic_settings_wireless"
       android:key="top_level_network"
       android:order="-150"
       android:title="@string/network_dashboard_title"
       android:summary="@string/summary_placeholder"
       settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
       ...省略很多Preference
<PreferenceScreen>

了解下Preference的通用XML元素属性

android:key  //唯一标识,SharedPreferences通过此Key值进行数保存
android:defaultValue //默认值
android:enabled  //是否可用
android:icon //图标
android:title //大标题
android:summary //小标题(摘要)
android:layout //布局
android:widgetLayout //小部件部分的布局
android:persistent /Preference元素是否保存sharedpreferences文件中。
android:order //顺序值越大,优先级越高,菜单选项排列越前
android:fragment //点击需要启动的android:fragment 
android:dependency //一个Preference的可用状态依赖于另一个Preference
android:disableDependentsState //与android:dependency相反

可见主界面列表项是由一个个Preference构成,比如网络互联网,其中android:fragment指明了该选项加载的Fragment是NetworkDashboardFragment,settings:controller指明了每一项的功能以及对二级菜单项的控制。这种通过xml布局加载选项方式就是静态方式加载配置如图

在这里插入图片描述
TopLevelSettings类定义如何启动二级页面

    @Override
    public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
    	//[android] 回调OnPreferenceStartFragmentCallback接口方法初始化并启动目标首选项
        new SubSettingLauncher(getActivity())
                .setDestination(pref.getFragment())
                .setArguments(pref.getExtras())
                .setSourceMetricsCategory(caller instanceof Instrumentable
                        ? ((Instrumentable) caller).getMetricsCategory()
                        : Instrumentable.METRICS_CATEGORY_UNKNOWN)
                .setTitleRes(-1)
                //[android] 使用建造者模式构建启动参数最终调用startActivity(intent),目标SubSettings
                .launch();
        return true;
    }

LaunchRequest作为SubSettingsLauncher启动器内部类,封装了目标intent和参数等启动信息通过建造者模式统一启动二级页面SubSettings。

public Intent toIntent() {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        copyExtras(intent);
        //[android]所有二级页面Activity都是SubSettings.class
        intent.setClass(mContext, SubSettings.class);
        if (TextUtils.isEmpty(mLaunchRequest.destinationName)) {
            throw new IllegalArgumentException("Destination fragment must be set");
        }
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, mLaunchRequest.destinationName);

        if (mLaunchRequest.sourceMetricsCategory < 0) {
            throw new IllegalArgumentException("Source metrics category must be set");
        }
        intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
                mLaunchRequest.sourceMetricsCategory);

        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, mLaunchRequest.arguments);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
                mLaunchRequest.titleResPackageName);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
                mLaunchRequest.titleResId);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, mLaunchRequest.title);
        intent.addFlags(mLaunchRequest.flags);
        intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
                mLaunchRequest.transitionType);

        return intent;
    }
	/**
     * Simple container that has information about how to launch a subsetting.
     */
    static class LaunchRequest {
    	//[android]启动信息
        String destinationName; //标题资源Id
        int titleResId; //标题资源Id
        String titleResPackageName;
        CharSequence title; //标题
        int sourceMetricsCategory = -100; //指标类别 > 0
        int flags;
        Fragment mResultListener;
        int mRequestCode;
        UserHandle userHandle;
        int transitionType;
        Bundle arguments;
        Bundle extras;
    }
public class SubSettings extends SettingsActivity {

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

    @Override
    protected boolean isValidFragment(String fragmentName) {
        Log.d("SubSettings", "Launching fragment " + fragmentName);
        return true;
  
protected boolean isValidFragment(String fragmentName) {
        // Almost all fragments are wrapped in this,
        // except for a few that have their own activities.
        //[android]ENTRY_FRAGMENTS数组维护多个有效的fragment,如果要在子页面新加fragment,要注册在SettingsGateway的ENTRY_FRAGMENTS数组中。
        for (int i = 0; i < SettingsGateway.ENTRY_FRAGMENTS.length; i++) {
            if (SettingsGateway.ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
        }
        return false;
    }
protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        Log.d(LOG_TAG, "Starting onCreate");
        long startTime = System.currentTimeMillis();

        final FeatureFactory factory = FeatureFactory.getFactory(this);

		//[android]以抽象工厂方式提供Dashboard特性
        mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);

        // Should happen before any call to getIntent()
        //[android]获取定义在manifest名为com.android.settings.FRAGMENT_CLASS的meta-data
        getMetaData();

		//[android]检查Intent EXTRA_SHOW_FRAGMENT, 内部重新包裹了Intent。
        final Intent intent = getIntent();
        if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
            getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
        }

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

		//[android]判断是否是SubSettings
        // 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 this is in setup flow, don't apply theme. Because light theme needs to be applied
        // in SettingsBaseActivity#onCreate().
        if (isSubSettings &amp;&amp; !WizardManagerHelper.isAnySetupWizard(getIntent())) {
            setTheme(R.style.Theme_SubSettings);
        }

		//[android]设置Activity:SubSettings的布局
        setContentView(R.layout.settings_main_prefs);

		//[android]监控fragment回退栈状态改变
        getSupportFragmentManager().addOnBackStackChangedListener(this);

        if (savedState != null) {
            // We are restarting from a previous saved state; used that to initialize, instead
            // of starting fresh.
            //[android]可以从EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,EXTRA_SHOW_FRAGMENT_TITLE,EXTRA_SHOW_FRAGMENT_TITLE_RESID获取页面标题
            setTitleFromIntent(intent);

            ArrayList<DashboardCategory> categories =
                    savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
            if (categories != null) {
                mCategories.clear();
                mCategories.addAll(categories);
				//[android]再从fragment回退栈中找标题
                setTitleFromBackStack();
            }
        } else {
        	//[android]启动一个新的fragment
            launchSettingFragment(initialFragmentName, intent);
        }

        final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());

		//[android]actionBar和其它按钮控件初始化
        final actionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(!isInSetupWizard);
            actionBar.setHomeButtonEnabled(!isInSetupWizard);
            actionBar.setDisplayShowTitleEnabled(true);
        }
        mMainSwitch = findViewById(R.id.switch_bar);
        if (mMainSwitch != null) {
            mMainSwitch.setMetricsTag(getMetricsTag());
            mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1);
        }

        if (DEBUG_TIMING) {
            Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
        }
    }

可以看出二级页面都会启动一个Fragment,看下launchSettingFragment(initialFragmentName, intent)这个方法

	void launchSettingFragment(String initialFragmentName, Intent intent) {
        if (initialFragmentName != null) {
            setTitleFromIntent(intent);

            Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
			//[android]通过验证设置面包屑标题并加载一个新的fragment
            switchToFragment(initialFragmentName, initialArguments, true,
                    mInitialTitleResId, mInitialTitle);
        } else {
            // Show search icon as up affordance if we are displaying the main Dashboard
            mInitialTitleResId = R.string.dashboard_title;
			//[android]加载主界面fragment
            switchToFragment(TopLevelSettings.class.getName(), null /* args */, false,
                    mInitialTitleResId, mInitialTitle);
        }
    }

启动的Fragment名称就是EXTRA_SHOW_FRAGMENT=“:settings:show_fragment”指定的值,所传递参数由EXTRA_SHOW_FRAGMENT_ARGUMENTS=”:settings:show_fragment_args指定
比如启动的二级子页面:
在这里插入图片描述

总结

原文地址:https://blog.csdn.net/qq_23069607/article/details/127301432

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_48582.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注