结论
- 默认情况下横竖屏切换时,Activity 会销毁并重新创建
- 如果想保留某个对象,可以重写onRetainCustomNonConfigurationInstance返回要保存的对象,然后再新的Activity中通过getLastNonConfigurationInstance方法获取到这个对象,
- 也可以通过 ViewModel 来保存要保留的对象,因为 ViewModel 会通过 onRetainNonConfigurationInstance 机制保留对象
-
添加 android:configChanges=”keyboardHidden orientation screenSize” 横竖屏切换只会调用 Activity.onConfigurationChanged 方法 - 横屏状态下,旋转 180 不会调用 Activity.onConfigurationChanged,可是采用 activity.window.decorView.addOnLayoutChangeListener 方式监听
默认情况下横竖屏切换的起点是
ActivityThread.handleRelaunchActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
@Override public void handleRelaunchActivity(ActivityClientRecord tmp, PendingTransactionActions pendingActions) { Configuration changedConfig = null; int configChanges = 0; synchronized (mResourcesManager) { if (mPendingConfiguration != null) { changedConfig = mPendingConfiguration; mPendingConfiguration = null; } } if (changedConfig != null) { handleConfigurationChanged(changedConfig, null);// 最终可能会调用 activity.onConfigurationChanged(configToReport); 默认情况下 Configuration.diffPublicOnly 计算没有修改,所以不会触发 onConfigurationChanged } ActivityClientRecord r = mActivities.get(tmp.token); r.activity.mConfigChangeFlags |= configChanges; r.mPreserveWindow = tmp.mPreserveWindow; r.activity.mChangingConfigurations = true; handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity"); }
ActivityThread.handleRelaunchActivityInner
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/** * 从代码可以了解到内部执行了 performPauseActivity callActivityOnStop handleDestroyActivity 等方法,所以 Activity 会走一遍销毁的生命周期; * 最后会调用 handleLaunchActivity 方法重新创建一个Activity,走一遍启动 Activity 的生命周期 */ private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents, PendingTransactionActions pendingActions, boolean startsNotResumed, Configuration overrideConfig, String reason) { final Intent customIntent = r.activity.mIntent; if (!r.paused) { performPauseActivity(r, false, reason, null /* pendingActions */); } if (!r.stopped) { callActivityOnStop(r, true /* saveState */, reason); } handleDestroyActivity(r.token, false, configChanges, true, reason); r.startsNotResumed = startsNotResumed; r.overrideConfig = overrideConfig; handleLaunchActivity(r, pendingActions, customIntent); }
ActivityThread.handleDestroyActivity
1 2 3 4 5 6 7 8 9 10
@Override public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { // destroy activity ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason); WindowManager wm = r.activity.getWindowManager(); wm.removeViewImmediate(v);// 这个地方最终会调用 Activity.onDetachedFromWindow ActivityTaskManager.getService().activityDestroyed(token); }
ActivityThread.performDestroyActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); Class<? extends Activity> activityClass = null; r.activity.mFinished = true; // 如果是横竖屏切换 会调用 retainNonConfigurationInstances 获取需要保留的数据 if (getNonConfigInstance) { r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); } mInstrumentation.callActivityOnDestroy(r.activity); // 调用 Acitvity.OnDestroy r.setState(ON_DESTROY); synchronized (mResourcesManager) { mActivities.remove(token); } return r; }
Activity 方法调用堆栈
竖屏切换成横屏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
⇢ onPause[]
⇠ onPause[0ms]="void"
⇢ onStop[]
⇠ onStop[0ms]="void"
⇢ onSaveInstanceState[outState="Bundle[{}]"]
⇠ onSaveInstanceState[0ms]="void"
⇢ onRetainCustomNonConfigurationInstance[]
⇠ onRetainCustomNonConfigurationInstance[0ms]="null"
⇢ isChangingConfigurations[]
⇠ isChangingConfigurations[0ms]="true"
⇢ onDestroy[]
⇠ onDestroy[0ms]="void"
⇢ onDetachedFromWindow[]
⇠ onDetachedFromWindow[0ms]="void"
⇢ <init>[]
⇠ <init>[1ms]="void"
⇢ onCreate[savedInstanceState="Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@b8c12cc, 2131230768=androidx.appcompat.widget.Toolbar$SavedState@e265915, 2131230770=android.view.AbsSavedState$1@b8c12cc, 2131230776=android.view.AbsSavedState$1@b8c12cc, 2131230839=android.view.AbsSavedState$1@b8c12cc, 2131230977=android.view.AbsSavedState$1@b8c12cc}}], androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:lastAutofillId=1073741823, android:fragments=android.app.FragmentManagerState@b6842a}]"]
⇢ getLastNonConfigurationInstance[]
⇠ getLastNonConfigurationInstance[0ms]="androidx.activity.ComponentActivity$NonConfigurationInstances@310b8f6"
⇠ onCreate[39ms]="void"
⇢ onStart[]
⇠ onStart[0ms]="void"
⇢ onRestoreInstanceState[savedInstanceState="Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@b8c12cc, 2131230768=androidx.appcompat.widget.Toolbar$SavedState@e265915, 2131230770=android.view.AbsSavedState$1@b8c12cc, 2131230776=android.view.AbsSavedState$1@b8c12cc, 2131230839=android.view.AbsSavedState$1@b8c12cc, 2131230977=android.view.AbsSavedState$1@b8c12cc}}], androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:lastAutofillId=1073741823, android:fragments=android.app.FragmentManagerState@b6842a}]"]
⇠ onRestoreInstanceState[0ms]="void"
⇢ onResume[]
⇠ onResume[2ms]="void"
⇢ onAttachedToWindow[]
⇠ onAttachedToWindow[0ms]="void"
⇢ onWindowFocusChanged[hasFocus="true"]
⇠ onWindowFocusChanged[0ms]="void"
横屏切换成竖屏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
⇢ onPause[]
⇠ onPause[1ms]="void"
⇢ onStop[]
⇠ onStop[0ms]="void"
⇢ onSaveInstanceState[outState="Bundle[{}]"]
⇠ onSaveInstanceState[0ms]="void"
⇢ onRetainCustomNonConfigurationInstance[]
⇠ onRetainCustomNonConfigurationInstance[0ms]="null"
⇢ isChangingConfigurations[]
⇢ onDetachedFromWindow[]
⇠ onDetachedFromWindow[0ms]="void"
⇢ <init>[]
⇠ <init>[0ms]="void"
⇢ onCreate[savedInstanceState="Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@b8c12cc, 2131230768=androidx.appcompat.widget.Toolbar$SavedState@776b8c, 2131230770=android.view.AbsSavedState$1@b8c12cc, 2131230776=android.view.AbsSavedState$1@b8c12cc, 2131230839=android.view.AbsSavedState$1@b8c12cc, 2131230977=android.view.AbsSavedState$1@b8c12cc}}], androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:lastAutofillId=1073741823, android:fragments=android.app.FragmentManagerState@fe5a2d5}]"]
⇢ getLastNonConfigurationInstance[]
⇠ getLastNonConfigurationInstance[0ms]="androidx.activity.ComponentActivity$NonConfigurationInstances@76a9751"
⇠ onCreate[34ms]="void"
⇢ onStart[]
⇠ onStart[1ms]="void"
⇢ onRestoreInstanceState[savedInstanceState="Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@b8c12cc, 2131230768=androidx.appcompat.widget.Toolbar$SavedState@776b8c, 2131230770=android.view.AbsSavedState$1@b8c12cc, 2131230776=android.view.AbsSavedState$1@b8c12cc, 2131230839=android.view.AbsSavedState$1@b8c12cc, 2131230977=android.view.AbsSavedState$1@b8c12cc}}], androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:lastAutofillId=1073741823, android:fragments=android.app.FragmentManagerState@fe5a2d5}]"]
⇠ onRestoreInstanceState[1ms]="void"
⇢ onResume[]
⇠ onResume[1ms]="void"
⇢ onAttachedToWindow[]
⇠ onAttachedToWindow[0ms]="void"
⇢ onWindowFocusChanged[hasFocus="true"] // 只要焦点从Activity中失去或者得到就会被调用,所以会被调用多次
⇠ onWindowFocusChanged[0ms]="void"
横竖屏切换不重新创建 Activity
Android 4.0 之前的版本
1
<activity android:configChanges="keyboardHidden|orientation"/>
Android 4.0 之后的版本
1
<activity android:configChanges="keyboardHidden|orientation|screenSize"/>
设置好如上参数时,横竖屏切换只会调用 Activity.onConfigurationChanged 方法
调用堆栈
1
2
3
4
ActivityThread.handleActivityConfigurationChanged
ActivityThread.performConfigurationChangedForActivity
ActivityThread.performActivityConfigurationChanged
Activity.onConfigurationChanged
横屏变竖屏
1
2
⇢ onConfigurationChanged[newConfig="{1.0 310mcc260mnc [zh_CN_#Hans,en_US] ldltr sw360dp w672dp h336dp 480dpi nrml long land finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 2160, 1080) mAppBounds=Rect(0, 0 - 2016, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_90} s.5}"]
⇠ onConfigurationChanged[2ms]="void"
竖屏变横屏
1
2
⇢ onConfigurationChanged[newConfig="{1.0 310mcc260mnc [zh_CN_#Hans,en_US] ldltr sw360dp w360dp h648dp 480dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1080, 2160) mAppBounds=Rect(0, 0 - 1080, 2016) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.7}"]
⇠ onConfigurationChanged[0ms]="void"