背景:當轉屏幕時,activity的頁面中封裝一個成員變量a,每次轉屏幕時,自加一,在onSaveInstanceState()方法中實現邏輯代碼,並在onCreate()方法中用EditText控件顯示遞增的成員變量a,但是出現EditText無法顯示的問題
問題源碼如下:
問題的解決:
- 安卓會自動保存某些view的狀態,旋轉屏幕->onSaveInstanceState保存了EditText的內容->然後在onCreate的時候其實已經給EditText設置成功了,但是隨後會在->onRestoreInstanceState嘗試恢復之前的值,所以造成了值沒改變。saveEnabled設爲false就不會恢復這個EditText的值。
-
源碼斷點分析
- {android:viewHierarchyState=Bundle[{android:Panels=android.util.SparseArray@4130d260, android:focusedViewId=2131034172, android:views=android.util.SparseArray@412fe3a0, android:ActionBar=android.util.SparseArray@412f21d8}],a=1}
- 追根到底[android.view.AbsSavedState$1@4120ae10, TextView.SavedState{412fe658 start=0 end=0 text=第一次啓動}, null, null, null, null, null, null, null, null, null, null, null]
問題:每次當android手機轉屏幕時,當前的activity會自動銷燬與重建,那麼在調用的生命週期的過程是怎麼樣的呢?
手機操作:
-
第一次啓動
- 修改EditText文字內容然後再轉屏幕
問題:那麼如何從源碼裏解釋呢? + 解決這個問題之前,我自己設想如果是我,我怎麼編?
-
這裏的應用場景是什麼:
如果因爲系統資源緊張而導致Activity的Destory, 系統會在用戶回到這個Activity時有這個Activity存在過的記錄,系統會使用那些保存的記錄數據(描述了當Activity被Destory時的狀態)來重新創建一個新的Activity實例。那些被系統用來恢復之前狀態而保存的數據被叫做 "instance state" ,它是一些存放在Bundle對象中的key-value pairs。
-
實例
Caution:你的Activity會在每次旋轉屏幕時被destroyed與recreated。當屏幕改變方向時,系統會Destory與Recreate前臺的activity,因爲屏幕配置被改變,你的Activity可能需要加載一些alternative的資源(例如layout).
-
特點:
- 我們不想Activity被結束,但是因爲某些原因(釋放內存資源等)系統會自動Kill了,這個不是我們能控制的,是系統級控制的,但是APP進程未被終結。
- 我們仍然需要這個Activity對象和原來保存的狀態,所以需要某個地方能保存這個對象或原來對象的狀態
- 再次回到Acitivity頁面時,我希望能再讀取原來的狀態並恢復
-
想想在這個過程中,我們可能需要什麼?
- 如果這是系統級控制,那麼系統必須有一個類或者有個模塊專門負責系統級事件的分發給指定的處理方法,例如將轉屏幕,語言配置改變等事件分發給void xxxA(){};
- 在銷燬Activity時,我們要得到這個頁面的引用,並遍歷該頁面,知道有哪些需要保存並且將來恢復頁面時需要恢復的,並保存在一個安全的地方,不會跟着對象的銷燬而銷燬。
-
整理轉屏幕這個事件發生的的流程
- 轉屏幕事件發生,系統監聽到,將消息分發到相應的處理器
-
處理器調用相關方法,這裏我們猜一下發生了什麼
- 系統先後調用onPause()方法、onSaveInstanceState()方法、onStop()、onDestroy()、onCreate()、onStrat()、onRestoreInstanceState()、onResume().(以上各方法定爲方一)
- 方一讓頁面暫停業務邏輯,方二保存所需對象的狀態,方三釋放某些資源,方四開始銷燬頁面,方五開始再次創建所需對象、方七開始獲得原來對象的狀態,方八開始恢復業務邏輯
-
現在我們帶着疑問去找源碼
- 系統監聽到事件是在哪裏?
- onSaveInstanceState()做了什麼事?
源碼
protected void onSaveInstanceState(Bundle outState) { outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());//封裝當前窗口布局的層級結構 Parcelable p = mFragments.saveAllState();//這裏保存了所有的狀態 if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p);//??? } getApplication().dispatchActivitySaveInstanceState(this, outState);//將該頁面也傳遞的outState綁定在一起 }
-
protected void onRestoreInstanceState(Bundle savedInstanceState) { if (mWindow != null) {//當前窗口存在 Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);//找到之前保存的Bundle對象 if (windowState != null) { mWindow.restoreHierarchyState(windowState);//恢復之前的狀態 } } }
-基於View的不同組件重寫View的getDefauleEditable()方法,使其在上述情況時,是否應該要自動保存內容。EditText組件在轉屏幕時沒有顯示出原來的效果就是因爲以下代碼
protected boolean getDefaultEditable() { return false; }
PS:還有一些限於自身能力,還無法解決。但假以時日,必能明白其中原理。