Activity橫豎屏切換生命周期
/**
* onCreate : 創建activity時調用。設置在該方法中,還以Bundle中可以提出用於創建該 Activity 所需的信息
* onStart : activity變為在屏幕上對用戶可見時,即獲得焦點時,會調用
* onResume : activity開始與用戶交互時調用(無論是啟動還是重新啟動一個活動,該方法總是被調用的)
* onPause : activity被暫停或收回cpu和其他資源時調用,該方法用於保存活動狀態的
* onStop : activity被停止並轉為不可見階段及后續的生命周期事件時,即失去焦點時調用
* onDestroy : activity被完全從系統內存中移除時調用,該方法被調用可能是因為有人直接調用 finish()方法 或者系統決定停止該活動以釋放資源
*
* onSaveInstanceState : 不是生命周期方法,只有在由系統銷毀一個Activity時,會被調用
* onRestoreInstanceState : 不是生命周期方法,只有在activity被系統回收,重新創建activity的情況下才會被調用
* onConfigurationChanged : 不是生命周期方法,當系統的配置信息發生改變時,系統會調用此方法
*
*/
一、屏幕橫豎屏切換的代碼
豎屏:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
橫屏:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
獲得當前屏幕狀態:
getResources().getConfiguration().orientation
此狀態可為:Configuration.ORIENTATION_PORTRAIT
和Configuration.ORIENTATION_LANDSCAPE
二、橫豎屏切換Activity生命周期回調
橫豎屏屬性可以在AndroidManifest.xml中設置,也可以在MainActivity.java中進行設置(上面已經提及)。
本節主要講解在AndroidManifest.xml中的設置
<activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait"> </activity>
(一)configChanges屬性
設置configChanges這個值可以避免Activity生命周期被回到。該部分具體的參數:
- orientation:屏幕在縱向和橫向間旋轉
- keyboardHidden:鍵盤顯示或隱藏
- screenSize:屏幕大小改變
- fontScale:用戶變更了首選字母大小
- locale:用戶選擇了不同的語言設定
- keyboard:鍵盤類型變更,如手機從九宮格鍵盤變為全鍵盤
- touchscreen或navigation:鍵盤或導航方向變換,一般不會發生這種情況。
前面三個是常用的,后面屬性很少使用
如果要activity中的生命周期不回調,就要設置:
android:configChanges="orientation|keyboardHidden|screenSize"
缺少其中任一一個都會Activity生命周期回調,即如下情況:
不發生回調是如下情況:
在這附上對應的代碼:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "onCreate"); Button bt = (Button)findViewById(R.id.bt_skip_other_activity); bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); else setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } }); } @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i(TAG, "onConfigurationChanged"); initChange(); } private void initChange(){ if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) Log.i(TAG, "屏幕改變,當前為豎屏"); else Log.i(TAG, "屏幕改變,當前為橫屏"); } @Override protected void onRestart() { super.onRestart(); Log.i(TAG, "onRestart"); } @Override protected void onStart() { super.onStart(); Log.i(TAG, "onStart"); } @Override protected void onStop() { super.onStop(); Log.i(TAG, "onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy"); } @Override protected void onPause() { super.onPause(); Log.i(TAG, "onPause"); } @Override protected void onResume() { super.onResume(); Log.i(TAG, "onResume"); } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); Log.i(TAG, "onSaveInstanceState"); } @Override protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.i(TAG, "onRestoreInstanceState"); } }
onSaveInstanceState() 和 onRestoreInstanceState() 方法
現在我們再回過頭來看這兩個方法。
1. 基本作用:
首先要聲明的是:Activity的 onSaveInstanceState() 和 onRestoreInstanceState() 並不是生命周期方法,它們不同於 onCreate()、onPause()等生命周期方法,並不一定會被觸發。當應用遇到意外情況(如:內存不足、按Home鍵等)由系統銷毀一個Activity時,onSaveInstanceState() 會被調用。但當用戶主動去銷毀一個Activity時,例如在應用中按返回鍵,onSaveInstanceState() 就不會被調用。此時,用戶的行為決定了不需要保存Activity的狀態。通常onSaveInstanceState() 只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。
比如說,當我們想在切換橫豎屏的時候保存視頻的播放進度。這時我們就可以在 Activity 中重寫 onSaveInstanceState(Bundle outState) 方法。然后調用 outState.putXXX() 來保存數據。然后在 Activity 重新被創建時在 onCreate(Bundle savedInstanceState) 或 onRestoreInstanceState(Bundle savedInstanceState) 中 調用 savedInstanceState.getXXX() 來獲取數據。
這就是 onSaveInstanceState() 和 onRestoreInstanceState() 兩個函數的基本作用和用法了。
2. onSaveInstanceState() 什么時候調用
先看Application Fundamentals上的一段話:
Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key).
當activity變得容易被系統銷毀前,會回調 onSaveInstanceState() 方法,除非該 activity 是被用戶主動銷毀的(比如按下BACK鍵)。
何為"容易"? 有這么幾種情況:
(1)、當用戶按下HOME鍵時。
這是顯而易見的,系統不知道你按下HOME后要運行多少其他的程序,自然也不知道activity A是否會被銷毀,因此系統會調用onSaveInstanceState(),讓用戶有機會保存某些非永久性的數據。以下幾種情況的分析都遵循該原則
(2)、長按HOME鍵,選擇運行其他的程序時。
(3)、按下電源按鍵(關閉屏幕顯示)時。
(4)、從 activity A 中啟動一個新的 activity 時。
(5)、屏幕方向切換時,例如從豎屏切換到橫屏時。
在屏幕切換之前,系統會銷毀 activity A,在屏幕切換之后系統又會自動地創建 activity A,所以onSaveInstanceState()一定會被執行,且也一定會執行onRestoreInstanceState()。
總而言之,onSaveInstanceState()的調用遵循一個重要原則,即當系統存在 “未經你許可” 銷毀了我們的 activity 的可能時,則 onSaveInstanceState() 會被系統調用,這是系統的責任,因為它必須要提供一個機會讓你保存你的數據。且調用將發生在 onPause() 之前。
3. onRestoreInstanceState()什么時候調用
onRestoreInstanceState() 被調用的前提是,activity A “確實” 被系統銷毀了,且 activity A 被重新創建。當 activity A 未被重新創建時,該方法不會被調用。例如,當正在顯示 activity A 的時候,用戶按下 HOME 鍵回到主界面,然后用戶緊接着又返回到 activity A,這種情況下 activity A 一般不會因為內存的原因被系統銷毀,故 onRestoreInstanceState() 方法不會被執行, 這也證明這兩個方法不一定會成對被使用。
onRestoreInstanceState() 在 onStart() 和 onResume() 之間調用。
4. onSaveInstanceState()方法的默認實現
如果我們沒有覆寫 onSaveInstanceState() 方法, 此方法的默認實現會自動保存 activity 中的某些狀態數據, 比如 activity 中各種 UI 控件的狀態.。android 應用框架中定義的幾乎所有 UI 控件都恰當的實現了 onSaveInstanceState() 方法,因此當 activity 被摧毀和重建時, 這些 UI 控件會自動保存和恢復狀態數據. 比如 EditText 控件會自動保存和恢復輸入的數據,而 CheckBox 控件會自動保存和恢復選中狀態.開發者只需要為這些控件指定一個唯一的 ID (通過設置 android:id 屬性即可), 剩余的事情就可以自動完成了.如果沒有為控件指定 ID, 則這個控件就不會進行自動的數據保存和恢復操作。
由上所述, 如果我們需要覆寫onSaveInstanceState()方法, 一般會在第一行代碼中調用該方法的默認實現:super.onSaveInstanceState(outState)。
5. 是否需要重寫onSaveInstanceState()方法
既然該方法的默認實現可以自動的保存UI控件的狀態數據, 那什么時候需要覆寫該方法呢?
如果需要保存額外的數據時, 就需要覆寫onSaveInstanceState()方法。大家需要注意的是:onSaveInstanceState()方法只適合保存瞬態數據, 比如UI控件的狀態, 成員變量的值等,而不應該用來保存持久化數據,如果當用戶離開當前 activity 時需要保存數據,應該在 onPause() 中保存(比如將數據保存到數據庫或文件中)。且 onPause() 中不適合做耗時的操作。
由於onSaveInstanceState()方法方法不一定會被調用, 因此不適合在該方法中保存持久化數據, 例如向數據庫中插入記錄等. 保存持久化數據的操作應該放在onPause()中。若是永久性值,則在onPause()中保存;若大量,則另開線程吧,別阻塞UI線程。
6. 引發activity銷毀和重建的其它情況
除了系統處於內存不足的原因會摧毀 activity 之外, 某些系統設置的改變也會導致 activity 的摧毀和重建. 例如改變屏幕方向(見上), 改變語言設定, 鍵盤彈出等。
android:configChanges 屬性
VALUE DESCRIPTION
"mcc" 國際移動用戶識別碼所屬國家代號是改變了
"mnc" 國際移動用戶識別碼的移動網號碼是改變了
"locale" 地址改變了-----用戶選擇了一個新的語言會顯示出來
"touchscreen" 觸摸屏是改變了------通常是不會發生的
"keyboard" 鍵盤發生了改變----例如用戶用了外部的鍵盤
"keyboardHidden" 鍵盤的可用性發生了改變
"navigation" 導航發生了變化-----通常也不會發生
"screenLayout" 屏幕的顯示發生了變化------不同的顯示被激活
"fontScale" 字體比例發生了變化----選擇了不同的全局字體
"uiMode" 用戶的模式發生了變化
"orientation" 屏幕方向改變了
"screenSize" 屏幕大小改變了
"smallestScreenSize" 屏幕的物理大小改變了,如:連接到一個外部的屏幕上
以上是 android:configChanges 屬性的所有值。當我們希望一種或者多種配置改變時避免重新啟動 activity。就可以通過在 AndroidManifest 中設置 android:configChanges 屬性來實現。如下所示:
<activity
android:name=".XXXActivity"
android:configChanges="XXX|XXX"/>
我們可以在這里聲明 activity 可以處理的任何配置改變,當這些配置改變時不會重新啟動activity,而會調用 onConfigurationChanged() 方法。如果改變的配置中包含了你所無法處理的配置(在android:configChanges並未聲明),你的 activity 仍然要被重新啟動。
當 Configuration 改變后,ActivityManagerService 將會發送"配置改變"的廣播,會要求 ActivityThread 重新啟動當前 focus 的 Activity。但當我們為 activity 配置了 configChanges 屬性,那么 activity 就不會被銷毀再重新創建,而是會回調 onConfigurationChanged 方法。
================== End