The first step is to find the entrance Setting
By AndroidManifest.xml file, we can know Settings.java entrance Setting entire program.
Open Settings.java may find that it inherited from SettingsActivity.java
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 SimSettingsActivity extends SettingsActivity { /* empty */ } public static class TetherSettingsActivity extends SettingsActivity { /* empty */ } public static class VpnSettingsActivity extends SettingsActivity { /* empty */ } ...... ......
Note annotation (ps: to set a separate subclass start) we can know the reason for the definition of these classes inherited from SettingsActivity in Settings in the Setting is used to set an internal option to start a separate, for example, long press the status bar WIFI small icon to jump to the WiFi settings.
Here we open SettingsActivity.java
public class SettingsActivity extends SettingsDrawerActivity implements PreferenceManager.OnPreferenceTreeClickListener, PreferenceFragment.OnPreferenceStartFragmentCallback, ButtonBarHandler, FragmentManager.OnBackStackChangedListener { private static final String LOG_TAG = "SettingsActivity";
It inherited SettingsDrawerActivity (ps: Path /frameworks/base/package/SettingsLib/src/com/android/settingslib/drawer/SettingsActivity.java)
First seen onResume () method
@Override protected void onResume() { super.onResume(); 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"); registerReceiver(mPackageReceiver, filter); new CategoriesUpdateTask().execute(); }
The main added package install, remove, modify, update, monitor, registered radio, start the thread last update.
Went CategoriesUpdateTask snippet
private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> { private final CategoryManager mCategoryManager; public CategoriesUpdateTask() { mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this); } @Override protected Void doInBackground(Void... params) { mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this, getSettingPkg()); return null; } @Override protected void onPostExecute(Void result) { mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist); onCategoriesChanged(); } }
Automatically invoked when CategoriesUpdateTask inherited AsyncTask, unaware of AsyncTask students can learn, doInBackground thread will execute execute (), onPostExecute in the execute () after the call to execute, execute () method to be called in the main thread.
@Override public void setContentView(@LayoutRes int layoutResID) { final ViewGroup parent = findViewById(R.id.content_frame); if (parent != null) { parent.removeAllViews(); } LayoutInflater.from(this).inflate(layoutResID, parent); } @Override public void setContentView(View view) { ((ViewGroup) findViewById(R.id.content_frame)).addView(view); } @Override public void setContentView(View view, ViewGroup.LayoutParams params) { ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params); }
Here rewrite setContentView () method, so that subclasses can be added to the interface content_frame when calling setContextView.
SettingsDrawerActivity main set of the child and monitor refresh, do not do too much description here, let us continue to return SettingsActivity
See onCreate () code snippet
@Override protected void onCreate(Bundle savedState) { super.onCreate(savedState); Log.d(LOG_TAG, "Starting onCreate"); long startTime = System.currentTimeMillis(); final FeatureFactory factory = FeatureFactory.getFactory(this); mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this); // Should happen before any call to getIntent() getMetaData(); 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 = intent.getStringExtra(EXTRA_SHOW_FRAGMENT); 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); } setContentView(mIsShowingDashboard ? R.layout.settings_main_dashboard : R.layout.settings_main_prefs); mContent = findViewById(R.id.main_content); getFragmentManager().addOnBackStackChangedListener(this); if (savedState != null) { // We are restarting from a previous saved state; used that to initialize, instead // of starting fresh. setTitleFromIntent(intent); ArrayList<DashboardCategory> categories = savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES); if (categories != null) { mCategories.clear(); mCategories.addAll(categories); setTitleFromBackStack(); } } else { launchSettingFragment(initialFragmentName, isSubSettings, intent); } final boolean deviceProvisioned = Utils.isDeviceProvisioned(this); if (mIsShowingDashboard) { findViewById(R.id.search_bar).setVisibility( deviceProvisioned ? View.VISIBLE : View.INVISIBLE); findViewById(R.id.action_bar).setVisibility(View.GONE); final Toolbar toolbar = findViewById(R.id.search_action_bar); FeatureFactory.getFactory(this).getSearchFeatureProvider() .initSearchToolbar(this, toolbar); setActionBar(toolbar); // Please forgive me for what I am about to do. // // Need to make the navigation icon non-clickable so that the entire card is clickable // and goes to the search UI. Also set the background to null so there's no ripple. View navView = toolbar.getNavigationView(); navView.setClickable(false); navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); navView.setBackground(null); } ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(deviceProvisioned); actionBar.setHomeButtonEnabled(deviceProvisioned); actionBar.setDisplayShowTitleEnabled(!mIsShowingDashboard); } mSwitchBar = findViewById(R.id.switch_bar); if (mSwitchBar != null) { mSwitchBar.setMetricsTag(getMetricsTag()); } // 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, null); finish(); } }); Button skipButton = (Button) findViewById(R.id.skip_button); skipButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { setResult(RESULT_OK, null); finish(); } }); mNextButton = (Button) findViewById(R.id.next_button); mNextButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { setResult(RESULT_OK, null); 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); } } }
the getMetaData () method, ps: for获取activity 的meta-data字段
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()); } }
mFragmentClass obtained value corresponding meta-data, i.e. full path name of the package of Fragment; META_DATA_KEY_FRAGMENT_CLASS = "com.android.settings.FRAGMENT_CLASS".
Then see getIntent (), SettingsActivity rewrite this method.
@Override public Intent getIntent() { Intent superIntent = super.getIntent(); 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) { Intent modIntent = new Intent(superIntent); modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 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; }
The code is not complicated, in essence, is going to jump interface assigned to EXTRA_SHOW_FRAGMENT.
OnCreate continue setContentView went down ()
setContentView(mIsShowingDashboard ?
R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
mIsShowingDashboard is to judge whether the display interface to the class name and the value Settings consistent results, display different layout.
When you set Title, if savedState is not empty, it will get from the intent of the title, otherwise launchSettingFragment direct () method
@VisibleForTesting void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) { if (!mIsShowingDashboard && initialFragmentName != null) { setTitleFromIntent(intent); Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); switchToFragment(initialFragmentName, initialArguments, true, false, mInitialTitleResId, mInitialTitle, false); } else { // Show search icon as up affordance if we are displaying the main Dashboard mInitialTitleResId = R.string.dashboard_title; switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false, mInitialTitleResId, mInitialTitle, false); } }
Similarly, there is a determination to be the main interface or subclass Setting screen, and select a different title.
There is a switchToFragment () method, filling contents into the main content, the method used is the transaction.
/** * 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) { Log.d(LOG_TAG, "Switching to fragment " + fragmentName); 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(); Log.d(LOG_TAG, "Executed frag manager pendingTransactions"); return f; }
OnCreate continue to go down, judge Setting main interface or sub-class interface, to decide whether to set the search bar (considering whether the device supports), then the various buttons appear at the top of the interface loading process ends.