savedInstanceState和 fragment.setRetainInstance以及 viewmodel的區別

weixin_33935777發表於2018-12-12

以預設activity的配置 在螢幕旋轉的時候,一般activty都會被重建,以這個情況為例子來說明 Bundle savedInstanceState 和 fragment.setRetainInstance 以及 viewmodel的區別

0. 轉載請註明原文出處

作者github :github.com/zjw-swun 歡迎相互關注

1. 為什麼要把這3個放在一塊說

Bundle savedInstanceState 和 fragment.setRetainInstance 以及 viewmodel(viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);用法而不是自己new的那種),都具備一種功能,就是當 螢幕旋轉的時候( 以預設activity的配置前提),都能儲存一些要被銷燬掉的activity中的一些資料(如editext文字,以及recyclerView的滑動位置等),那麼這3個有什麼區別嗎,會不會因為我們不知道原理而踩坑,下文給出答案。

2. 結論

  1. Bundle savedInstanceState 中的資料是由系統程式進行儲存的,它能儲存的資料容量大小有限(例如intent中如果傳輸Bundle內容過大會出現異常),但是比如自己app因為手機記憶體不足而殺掉程式的話,可以能夠利用該方式進行資料還原 2.fragment.setRetainInstance 以及viewmodel(viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);用法而不是自己new的那種)的原理是一樣的,都是利用,Activity類的NonConfigurationInstances類在app程式中進行儲存的,它能儲存資料容量比Bundler savedInstanceState 方式要大,但是比如自己app因為手機記憶體不足而殺掉程式的話,則不能用該方式進行資料還原

3. 部分原始碼分析

以下以activity類中NonConfigurationInstances類如何在螢幕旋轉的時候( 以預設activity的配置前提)如何儲存並恢復NonConfigurationInstances物件為例,剖析原理 以下截圖以及截圖中的程式碼api版本為28,執行環境官方api 28的模擬器上 測試程式碼就不貼了很簡單, 先貼一下debug斷點列表,有興趣可以試試,至於如何debug app以及debug系統程式,不知道的朋友可以看一下我另一篇文章https://www.imooc.com/article/21992 廢話不多說 放圖

然後看重建activity時候的斷點(截圖中截的是被殺死的activity 走到ondestroy的時候)

這裡說明一下上一個activty先走ondrstroy 然後才重建新的activity這裡和activity跳轉做一個區分,然後從上圖中可以發現被殺死的activity 走到ondestroy的時候中的r和重建時候傳入的引數r(ActivityThread類中的ActivityClientRecord型別)是同一個物件,看看一下這個ActivityClientRecord類的程式碼

static final class ActivityClientRecord {
       //...不重要引數
        Activity.NonConfigurationInstances lastNonConfigurationInstances;
        //...不重要引數;
        }
複製程式碼

這就是核心的原理程式碼了,那麼為什麼說是儲存在app程式中呢,根據斷點列表你會發現涉及到一個關鍵類叫做 ActivityRelaunchItem這個類

這個類的mActivityClientRecord就是app程式儲存的那個r了,這個r裡面包含了lastNonConfigurationInstances,當activty切換的時候,系統程式通過binder機制通知app程式的client物件也就是activityThread間接呼叫preExecute方法,進行儲存,然後當重建activity的時候再系統程式通過binder機制通知app程式的client物件也就是activityThread(由activityThread.h傳送H.RELAUNCH_ACTIVITY訊息通過handler機制)間接呼叫execute方法 下圖是activity重建時候呼叫execute函式呼叫棧截圖

重建activty後進行attach把上個被殺死的activity存下來的rlastNonConfigurationInstances再設定給新activity

中間省略的步驟雖然多,但是根據斷點列表走下來其實很清楚的。

授人以魚不如授人以漁

對於如何除錯app程式和系統程式,www.imooc.com/article/219… 一文中有具體操作,但是涉及到binder類是如何進行transact發訊息給別的程式以及如何execTransact處理別的程式訊息 的c++層的原理並沒有給出解答,這裡推薦一篇部落格 blog.csdn.net/innost/arti… 講解的還算不錯。

相關文章