零.前言
在工作遇到了系统切换主题后,应用页面出现重叠错乱,播放音频的时候出现了点击图标后出现声音停顿1S
后继续播放的问题…
一.生命周期流程
此时,Activity经历了销毁到创建,状态保存到恢复状态的过程,不同的手机有一定的差别。
二.源码分析(Android M)
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); // 保存Window
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);// 保存Fragment
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);// 恢复Window
}
}
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
// 恢复Fragment
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
三.引起的Fragment页面重叠错乱问题
1.原因:在activity onSaveInstanceState执行时activity保存了Window和Fragment的状态
onCreate时我们初始化并添加Fragment
onCreate和onRestoreInstanceState时恢复了保存的Fragment,综上造成了Fragment
页面重叠错乱
2.解决方式:
(1).如果在切换主题后没有必要恢复我们应用之前的状态,可以在onSaveInstanceState时直接不保存Fragment状态即可
static final String FRAGMENTS_TAG = "android:fragments";
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(FRAGMENTS_TAG, null);// 不保存Fragment状态
}
(2).如果需要恢复我们应用之前的状态,可以通过FragmentManager找到保存的Fragment进行复用
private void showFragment(int index) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// 找到保存的Fragment进行复用。
Fragment fragmentByTag = getSupportFragmentManager().findFragmentByTag(TAGS[index]);
// 执行了onDestory后mFragments数组对象被系统回收了
if (fragmentByTag != null && mFragments[index] == null) {
mFragments[index] = (AbstractMainFragment) fragmentByTag;
}
if (mFragments[index] == null) {
if (index == PLAYBACK) {
mFragments[index] = new PlaybackFragment();
} else if (index == LIBRARY) {
mFragments[index] = new LibraryFragment();
} else {
mFragments[index] = new MixFragment();
}
ft.add(R.id.fl_main, mFragments[index], TAGS[index]);
}
for (int i = 0; i < mFragments.length; i++) {
if (mFragments[i] != null && i != index) {
ft.hide(mFragments[i]);
}
}
ft.show(mFragments[index]).commitAllowingStateLoss();
mCurrentTab = index;
}
四.API说明
其实相应方法的API说明介绍的很清楚,要多看API( ̄. ̄)
/** * Called when the activity is starting. This is where most initialization * should go: calling {@link #setContentView(int)} to inflate the * activity's UI, using {@link #findViewById} to programmatically interact * with widgets in the UI, calling * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve * cursors for data being displayed, etc. * * <p>You can call {@link #finish} from within this function, in * which case onDestroy() will be immediately called without any of the rest * of the activity lifecycle ({@link #onStart}, {@link #onResume}, * {@link #onPause}, etc) executing. * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> * * @param savedInstanceState If the activity is being re-initialized after * previously being shut down then this Bundle contains the data it most * recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b> * * @see #onStart * @see #onSaveInstanceState * @see #onRestoreInstanceState * @see #onPostCreate */ protected void onCreate(@Nullable Bundle savedInstanceState) {}
/** * Called to retrieve per-instance state from an activity before being killed * * so that the state can be restored in {@link #onCreate} or * * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method * * will be passed to both). * * <p>This method is called before an activity may be killed so that when it * comes back some time in the future it can restore its state. For example, * if activity B is launched in front of activity A, and at some point activity * A is killed to reclaim resources, activity A will have a chance to save the * current state of its user interface via this method so that when the user * returns to activity A, the state of the user interface can be restored * via {@link #onCreate} or {@link #onRestoreInstanceState}. * * <p>Do not confuse this method with activity lifecycle callbacks such as * {@link #onPause}, which is always called when an activity is being placed * in the background or on its way to destruction, or {@link #onStop} which * is called before destruction. One example of when {@link #onPause} and * {@link #onStop} is called and not this method is when a user navigates back * from activity B to activity A: there is no need to call {@link #onSaveInstanceState} * on B because that particular instance will never be restored, so the * system avoids calling it. An example when {@link #onPause} is called and * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A: * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't * killed during the lifetime of B since the state of the user interface of * A will stay intact. * * <p>The default implementation takes care of most of the UI per-instance * state for you by calling {@link android.view.View#onSaveInstanceState()} on each * view in the hierarchy that has an id, and by saving the id of the currently * focused view (all of which is restored by the default implementation of * {@link #onRestoreInstanceState}). If you override this method to save additional * information not captured by each individual view, you will likely want to * call through to the default implementation, otherwise be prepared to save * all of the state of each view yourself. * * <p>If called, this method will occur before {@link #onStop}. There are * no guarantees about whether it will occur before or after {@link #onPause}. * * @param outState Bundle in which to place your saved state. * * @see #onCreate * @see #onRestoreInstanceState * @see #onPause */ protected void onSaveInstanceState(Bundle outState) {}
/** * This method is called after {@link #onStart} when the activity is * being re-initialized from a previously saved state, given here in * <var>savedInstanceState</var>. Most implementations will simply use {@link #onCreate} * to restore their state, but it is sometimes convenient to do it here * after all of the initialization has been done or to allow subclasses to * decide whether to use your default implementation. The default * implementation of this method performs a restore of any view state that * had previously been frozen by {@link #onSaveInstanceState}. * * <p>This method is called between {@link #onStart} and * {@link #onPostCreate}. * * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}. * * @see #onCreate * @see #onPostCreate * @see #onResume * @see #onSaveInstanceState */ protected void onRestoreInstanceState(Bundle savedInstanceState) { }