ViewModel和onSaveInstanceState

目前可能很多人已經(jīng)使用了jetpack,目前,我已經(jīng)完全將kotlin+jetpack用于項(xiàng)目中。項(xiàng)目開發(fā)完畢,現(xiàn)在回頭看看一些技術(shù)細(xì)節(jié)。

onSaveInstanceState()

onSaveInstanceState()UI組件停止運(yùn)行、且未被主動(dòng)銷毀時(shí)調(diào)用。如用戶點(diǎn)擊Home鍵跳轉(zhuǎn)到桌面、用戶跳轉(zhuǎn)到新的UI組件,該UI組件停止運(yùn)行。
若是主動(dòng)銷毀,onSaveInstanceState()不會(huì)被調(diào)用。如用戶主動(dòng)點(diǎn)擊返回按鈕、代碼調(diào)用finish()相關(guān)的函數(shù)。

系統(tǒng)可能因內(nèi)存過低、電量過低時(shí)回收在后臺(tái)運(yùn)行的UI組件(Activity/Fragment),用戶重新打開該UI組件時(shí),會(huì)恢復(fù)在onSaveInstanceState()保存的數(shù)據(jù)。

onSaveInstanceState()適合保存少量數(shù)據(jù)。具體能保存多少數(shù)據(jù),我并沒有在任何地方看見官方說的具體值,文章Saving UI state with ViewModel SavedState and Dagger
的作者Nimrod Dayan介紹說限制在1MB以下,盡可能是能夠恢復(fù)界面數(shù)據(jù)的最小數(shù)據(jù)集。

ViewModel

ViewModel能夠在配置更改時(shí)保留,如當(dāng)屏幕旋轉(zhuǎn)時(shí),UI組件重新構(gòu)建,重新得到的仍然是同一個(gè)ViewModel。ViewModel為何能在UI組件銷毀時(shí)存活,可以看文章每日一問 Activity 都重建了,你 Fragment憑什么活著?

ViewModel可以保存更多的數(shù)據(jù)在內(nèi)存中,這些數(shù)據(jù)通常是需要在UI上顯示的。在配置更改時(shí),UI組件只是臨時(shí)地銷毀,系統(tǒng)會(huì)立刻生成新的UI組件,之前存在的ViewModel會(huì)立即通知新的UI組件更新界面,這樣界面看起來就和配置更改前完全一樣了。

總結(jié)

雖然在配置更改時(shí),ViewModel并不會(huì)重新創(chuàng)建,但是在系統(tǒng)回收UI組件時(shí),ViewModel同樣會(huì)被銷毀,此時(shí)唯一能保存數(shù)據(jù)的只有onSaveInstanceState()(當(dāng)然還可以持久化數(shù)據(jù)到文件/網(wǎng)絡(luò)等)。

可以看出ViewModelonSaveInstanceState()的功能都是保存數(shù)據(jù),但是他們?cè)谀撤N層面上是完全不同的:

  1. onSaveInstanceState()適合保存能夠恢復(fù)UI組件的最小數(shù)據(jù)集;ViewModel保存UI組件顯示所需的所有數(shù)據(jù)和一些額外的數(shù)據(jù)。
  2. ViewModel能夠在配置更改時(shí)保留數(shù)據(jù);onSaveInstanceState()能夠在用戶臨時(shí)離開UI組件時(shí)保留數(shù)據(jù)。若需要用戶在長(zhǎng)時(shí)間離開UI組件時(shí)保存數(shù)據(jù),請(qǐng)使用數(shù)據(jù)持久化。
  3. ViewModelonSaveInstanceState()并不沖突,反而應(yīng)該結(jié)合使用。onSaveInstanceState()保存了恢復(fù)UI組件的最小數(shù)據(jù),ViewModel需要這些數(shù)據(jù)重新恢復(fù)UI組件需要的其他數(shù)據(jù)。

顯然,ViewModel在保存數(shù)據(jù)方面,解決了onConfigurationChanged()的問題,并沒有解決onSaveInstanceState()的問題。

ViewModel和onSaveInstanceState()結(jié)合使用

在沒有ViewModel之前,我們會(huì)將加載數(shù)據(jù)的邏輯放在UI組件中。UI組件啟動(dòng)需要的最少數(shù)據(jù)通常會(huì)保留在啟動(dòng)時(shí)傳入的數(shù)據(jù)集中,如Activity啟動(dòng)時(shí)會(huì)在Intent中存儲(chǔ)對(duì)應(yīng)的數(shù)據(jù)。

若下次啟動(dòng)該Activity需要的最少數(shù)據(jù)沒有任何修改,那么我們可以重復(fù)利用Intent,因?yàn)橄到y(tǒng)回收UI組件后重啟該組件,系統(tǒng)會(huì)保留同樣的Intent。此時(shí)我們就可以不使用onSaveInstanceState()

但是,若在UI組件存活時(shí),更改過該組件啟動(dòng)需要的最小數(shù)據(jù)集,那么就不能使用Intent了。我們就應(yīng)該在onSaveInstanceState()中保存最新的數(shù)據(jù)。

現(xiàn)代的組件架構(gòu)會(huì)將業(yè)務(wù)邏輯放在ViewModel中,所以重新啟動(dòng)UI組件的最小數(shù)據(jù)集需要被傳遞給ViewModelViewModel依賴這些數(shù)據(jù)來加載恢復(fù)UI組件的更多數(shù)據(jù)。此時(shí),我們就需要結(jié)合onSaveInstanceState()ViewModel使用了。

流程:

  1. ActivityIntent獲取數(shù)據(jù),或從onCreate(Bundle)中得到onSaveInstanceState()保存的數(shù)據(jù)
  2. 將這些數(shù)據(jù)賦值給ViewModel
  3. ViewModel使用這些數(shù)據(jù)加載UI組件顯示的數(shù)據(jù)

上面的流程雖然沒有任何問題,但是ViewModel的行為是完全依賴IntentonSaveInstanceState()的數(shù)據(jù),它并沒有將ViewModel和這些數(shù)據(jù)進(jìn)行強(qiáng)綁定。

我們期望在構(gòu)造ViewModel時(shí)就將這些數(shù)據(jù)傳遞給ViewModel使用。此時(shí)我們應(yīng)該使用ViewModelProvider.Factory。

如我們進(jìn)入一個(gè)文章詳情頁面:

class ArticleDetailViewModel(private val id: Long) : ViewModel() {
    class Factory(private val id: Long) : ViewModelProvider.Factory() {
        override fun create(modelClass: Class<T>): T {
            return modelClass.getConstructor(Long::class.java).newInstance(id)
        }
    }

    private val _article = MutableLiveData<Article>()

    val article: LiveData<Article>
        get() = _article
    // ...
}

class ArticleDetailActivity: AppCompatActivity() {
    private val articleDetailViewModel: ArticleDetailViewModel by viewModels(factoryProducer = {
             ArticleDetailViewModel.Factory(intent.getLong("articleId", -1L))
         }
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_article_detail)

        articleDetailViewModel.article.observe(this) { article ->
            // show article content
        }
        // ...
    }
}

這個(gè)文章詳情頁面依賴的最小數(shù)據(jù)集只有一個(gè)文章id。通過文章id,ViewModel加載文章的數(shù)據(jù),UI組件觀察到數(shù)據(jù)后進(jìn)行顯示。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容