(O)Settings分析之主界面加载流程

本文详细分析了Settings应用主界面的加载流程,从SettingsActivity的onCreate方法开始,包括SettingsDrawerActivity的设置、FeatureFactory的使用、DashboardSummary的生命周期,直至DashboardSummary的onCreateView和onViewCreated方法,讲解了如何初始化DashboardAdapter并更新UI。同时,文章还介绍了Settings在onResume方法中如何更新tiles列表和处理状态变化。
摘要由CSDN通过智能技术生成

1.    前言
本篇主要是为了记录Settings主界面的加载流程,为以后深入分析和记录在Settings应用做准备

2.    流程
    2.1    Settings 文件分析
    首先,我们要分析Settings应用主界面的加载工作,需要先确认哪个文件为此应用的主Activity,从AndroidManifest.xml文件中

<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>
	<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
</activity-alias>

因此,能够确定Settings应用的入口函数为Settings.java
遍观整个Settings.java文件,发现其继承了SettingsActivity.java文件,因此其整个生命周期均在SettingsActivity中进行实现的
因此我们进入Settings.java的生命周期

2.1.1 SettingsActivity的onCreate方法

@Override
		protected void onCreate(Bundle savedState) {
			// 1. 调用父类的onCreate方法
			super.onCreate(savedState);
			long startTime = System.currentTimeMillis();
		
			// 2. 初始化FeatureFactory,并且获取DashboardFeatureProvider
			final FeatureFactory factory = FeatureFactory.getFactory(this);
			mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
		
			// Should happen before any call to getIntent()
			// 3. 调用getMetaData方法
			getMetaData();
		
			// 4. 获取传入的参数数据
			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);
		
			final ComponentName cn = intent.getComponent();
			final String className = cn.getClassName();
			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) {
				setTheme(R.style.Theme_SubSettings);
			}
			// 5. 加载主界面
			setContentView(mIsShowingDashboard ?
					R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
		
			mContent = findViewById(R.id.main_content);
		
			getFragmentManager().addOnBackStackChangedListener(this);
			
			if (savedState != null) {
				......
			} else {
				launchSettingFragment(initialFragmentName, isSubSettings, intent);
			}
			......
		}

2.1.1.1 SettingsDrawerActivity的onCreate方法

protected void onCreate(@Nullable Bundle savedInstanceState) {
			......
			// 设置布局文件
			super.setContentView(R.layout.settings_with_drawer);
			......
		}

这边设置了整个Settings的主界面使用的是一个DrawerLayout布局,但是打开settings_with_drawer.xml文件,会发现,DrawerLayout只包含了一个布局,因此,即使使用了DrawerLayout,也没有抽屉
        2.1.1.2 FeatureFactory的本质
        从代码中看,首先调用的是FeatureFactory的静态方法getFactory,传入的参数为SettingsActivity

// 单例模式
public static FeatureFactory getFactory(Context context) {
	if (sFactory != null) {
		return sFactory;
	}
	......
	final String clsName = context.getString(R.string.config_featureFactory);
	if (TextUtils.isEmpty(clsName)) {
		throw new UnsupportedOperationException("No feature factory configured");
	}
	try {
		sFactory = (FeatureFactory) context.getClassLoader().loadClass(clsName).newInstance();
	} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
		throw new FactoryNotFoundException(e);
	}
	......
	return sFactory;
}
<string name="config_featureFactory" translatable="false">com.android.settings.overlay.FeatureFactoryImpl</string>

之后调用getDashboardFeatureProvider方法初始化DashboardFeatureProvider对象

@Override
public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
	if (mDashboardFeatureProvider == null) {
		mDashboardFeatureProvider = new DashboardFeatureProviderImpl(context);
	}
	return mDashboardFeatureProvider;
}

public DashboardFeatureProviderImpl(Context context) {
	mContext = context.getApplicationContext();
	mCategoryManager = CategoryManager.get(context, getExtraIntentAction());
	mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
	mPackageManager = context.getPackageManager();
}

2.1.1.3 调用getMetaData方法

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

从AndroidManifest.xml文件中可以看到,meta data fragment class的值是没有设定的,因此mFragmentClass的值为null
        2.1.1.4 获取传入的参数数据
        通过调用getIntent方法来获取从外部传入的参数数据

public Intent getIntent() {
	Intent superIntent = super.getIntent();
	String startingFragment = getStartingFragmentClass(superIntent);
	......
	if (startingFragment != null) {
		Intent modIntent = new Intent(superIntent);
		modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
		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);
		return modIntent;
	}
	return superIntent;
}

getStartingFragmentClass方法获取启动的Fragment

private String getStartingFragmentClass(Intent intent) {
	// 刚刚分析到,这个值是null
	if (mFragmentClass != null) return mFragmentClass;
		
	// 这个值获取到的结果是com.android.settings.Settings
	String intentClass = intent.getComponent().getClassName();
	// 因此此处直接返回null
	if (intentClass.equals(getClass().getName())) return null;
	......
}

回到getIntent方法中,直接返回super.getIntent
        2.1.1.5 加载主界面
        如上面所述,此时mIsShowingDashboard值为true,因此会直接使用settings_main_dashboard布局文件,然后因为savedState为null,因此会直接调用launchSettingFragment方法,其中,第一个参数为null,第二个参数为false。第三个参数为super.getIntent得到的值

void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
	// 由于mIsShowingDashboard为true,因此走else分支    
	if (!mIsShowingDashboard && initialFragmentName != null) {
		......
	} else {
		// Show search icon as up affordance if we are displaying the main Dashboard
		mDisplayHomeAsUpEnabled = true;
		mInitialTitleResId = R.string.dashboard_title;
		switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,mInitialTitleResId, mInitialTitle, false);
	}
}

调用switchToFragment方法,注意其的参数
        第一个参数为DashboardSummary的class
        第二个参数为null
        第三个参数为false
        第四个参数为false
        第五个参数为字串资源索引
        第六个参数为字串资源
        第七个参数为false
 打开switchToFragment方法

private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate, boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
	// 由于validate为false,因此直接跳过此方法
	if (validate && !isValidFragment(fragmentName)) {
		......
	}
	Fragment f = Fragment.instantiate(this, fragmentName, args);
	FragmentTransaction transaction = getFragmentManager().beginTransaction();
	transaction.replace(R.id.main_content, f);
	// false
	if (withTransition) {
		TransitionManager.beginDelayedTransition(mContent);
	}
	// false
	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;
}

由于此处第三个参数,即validate为false,因此直接会跳过抛出异常,即主界面跳过检查,直接使用DashboardSummary来替代main_content视图
        接下来就会直接进入DashboardSummary来处理Settings应用的主界面布局视图
    2.2    DashboardSummary分析
    从上面的分析,在Settings的onCreate方法中,将DashboardSummary初始化,并且当做Settings的主内容视图,因此,接下来需要看看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值