目前可能很多人已經(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ò)等)。
可以看出ViewModel和onSaveInstanceState()的功能都是保存數(shù)據(jù),但是他們?cè)谀撤N層面上是完全不同的:
-
onSaveInstanceState()適合保存能夠恢復(fù)UI組件的最小數(shù)據(jù)集;ViewModel保存UI組件顯示所需的所有數(shù)據(jù)和一些額外的數(shù)據(jù)。 -
ViewModel能夠在配置更改時(shí)保留數(shù)據(jù);onSaveInstanceState()能夠在用戶臨時(shí)離開UI組件時(shí)保留數(shù)據(jù)。若需要用戶在長(zhǎng)時(shí)間離開UI組件時(shí)保存數(shù)據(jù),請(qǐng)使用數(shù)據(jù)持久化。 -
ViewModel和onSaveInstanceState()并不沖突,反而應(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ù)集需要被傳遞給ViewModel。ViewModel依賴這些數(shù)據(jù)來加載恢復(fù)UI組件的更多數(shù)據(jù)。此時(shí),我們就需要結(jié)合onSaveInstanceState()和ViewModel使用了。
流程:
-
Activity從Intent獲取數(shù)據(jù),或從onCreate(Bundle)中得到onSaveInstanceState()保存的數(shù)據(jù) - 將這些數(shù)據(jù)賦值給
ViewModel -
ViewModel使用這些數(shù)據(jù)加載UI組件顯示的數(shù)據(jù)
上面的流程雖然沒有任何問題,但是ViewModel的行為是完全依賴Intent或onSaveInstanceState()的數(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)行顯示。