背景
activity
在配置變化(未配置configChanges)吵聪、內(nèi)存回收等情況下,頁面會(huì)重建兼雄。頁面在重新走一遍生命周期后吟逝,系統(tǒng)會(huì)幫我們恢復(fù)好控件的狀態(tài),具體原理可以看看《之前發(fā)布的文章》赦肋。
Activty澎办、Fragment
里定義的全局變量,需要我們手寫保存
- 在
onSaveInstanceState
里把變量保存到Bundle
- 在
onCreate
里判斷savedInstanceState
不為空金砍,則進(jìn)行恢復(fù)局蚀。示例代碼如下
class ManualSaveActivity : AppCompatActivity() {
lateinit var mPerson: Person
val PARAM_SAVE_KEY = "PARAM_KEY"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_manual_save)
mPerson = if (savedInstanceState != null) {
//從bundle恢復(fù)數(shù)據(jù)
savedInstanceState.getSerializable(PARAM_SAVE_KEY) as Person
} else {
//否則走默認(rèn)的初始化流程
Person("桑德蘭", 18, null)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//保存數(shù)據(jù)
outState.putSerializable(PARAM_SAVE_KEY, mPerson)
}
}
這種寫法可以滿足重建恢復(fù)數(shù)據(jù)的需求,但我們希望能更進(jìn)一步:通過框架恕稠,可以智能實(shí)現(xiàn)數(shù)據(jù)的保存琅绅、恢復(fù),業(yè)務(wù)代碼不需要關(guān)心這個(gè)過程鹅巍。
解決方案
Android的jetPack
架構(gòu)包里千扶,ViewModel
原生就支持配置變化自動(dòng)重建,再通過SavedStateHandle
擴(kuò)展就可以在內(nèi)存回收的時(shí)候骆捧,也能保存數(shù)據(jù)了澎羞。
ViewModel+SavedStateHandle
創(chuàng)建對(duì)象非常簡(jiǎn)單
ViewModelProvider(載體, SavedStateViewModelFactory(null, this)).get(AnswerViewModel::class.java)
只要activity、fragment
載體都指向activity
敛苇,就可以在fragment
共享數(shù)據(jù)妆绞。
SavedStateViewModelFactory
是能在內(nèi)存回收的時(shí)候保存數(shù)據(jù)關(guān)鍵,本質(zhì)上也是通過Bundle保存枫攀、恢復(fù)數(shù)據(jù)括饶。
開發(fā)實(shí)踐
我們選擇ViewModel+SavedStateHandle
這個(gè)組合,寫一個(gè)計(jì)數(shù)器DEMO進(jìn)行實(shí)踐来涨;為了展示ViewModel
能在Fragment
之間共享Activity
數(shù)據(jù)的能力图焰,我們的結(jié)構(gòu)是Activity
殼嵌套了Fragment
。
數(shù)據(jù)流轉(zhuǎn)過程是:Activity
請(qǐng)求數(shù)據(jù)蹦掐,Fragment
修改數(shù)據(jù)技羔。
ViewModel定義
可以看到實(shí)際存儲(chǔ)卧抗,是交給了SavedStateHandle
藤滥,開發(fā)者只用關(guān)心存取就可以
data class Person(
var name: String,
var age: Int,
var answerPosList:List<Int>?
):Serializable
class AnswerViewModel(private val savedStateHandler: SavedStateHandle) : ViewModel() {
private val KEY_PERSON = "KEY_PERSON"
fun setPerson(person: Person) {
savedStateHandler.set(KEY_PERSON, person)
}
fun getPerson(): Person? {
return savedStateHandler.get<Person>(KEY_PERSON)
}
}
Activity代碼
class SimpleActivity : AppCompatActivity() {
private lateinit var model: AnswerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mock(savedInstanceState)
setContentView(R.layout.activity_simple)
object : FragmentSwitcher<Int, Fragment>(supportFragmentManager, R.id.fl_simple_root) {
override fun generateFragment(key: Int?): Fragment {
return BlankFragment.newInstance(key.toString())
}
}.changeFragment(0)
}
//初始化
private fun mock(savedInstanceState: Bundle?) {
model = ViewModelProvider(this, SavedStateViewModelFactory(null, this)).get(
AnswerViewModel::class.java
)
if (model.getPerson() == null) {
Log.d("測(cè)試", "初始化")
val person = Person("桑德蘭", 18, null)
model.setPerson(person)
} else {
Log.d("測(cè)試", "不需要初始化" + model.getPerson())
}
}
companion object {
@JvmStatic
fun start(context: Context) {
val starter = Intent(context, SimpleActivity::class.java)
context.startActivity(starter)
}
}
}
Fragment核心代碼
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel = activity?.let {
ViewModelProvider(it, SavedStateViewModelFactory(null, this)).get(
AnswerViewModel::class.java
)
}!!
tvBind = view.findViewById(R.id.tv_blank_hello)
tvBind?.setOnClickListener(this)
renderUser()
}
override fun onClick(v: View?) {
val id = v?.id
if (id == R.id.tv_blank_hello) {
mViewModel.getPerson()?.age= mViewModel.getPerson()!!.age+1
renderUser()
}
}
private fun renderUser() {
tvBind?.text = "第${mArgPos}頁,姓名:${mViewModel.getPerson()?.name} \n 年齡:${mViewModel.getPerson()?.age}"
}
流程分析
系統(tǒng)SavedStateHandle
大致流程和我們手寫保存一樣颗味。
1.onSaveInstanceState
的時(shí)候保存到系統(tǒng)的bundle
里
performSave 被Activity超陆、Fragment調(diào)用
androidx.activity.ComponentActivity
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
Lifecycle lifecycle = getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
}
super.onSaveInstanceState(outState);
mSavedStateRegistryController.performSave(outState);
}
Fragment
void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
mSavedStateRegistryController.performSave(outState);
Parcelable p = mChildFragmentManager.saveAllState();
if (p != null) {
outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
}
}
2. onCreate
的時(shí)候根據(jù)savedInstanceState
恢復(fù)。
performRestore 被Activity、Fragment調(diào)用
androidx.activity.ComponentActivity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedStateRegistryController.performRestore(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
if (mContentLayoutId != 0) {
setContentView(mContentLayoutId);
}
}
Fragment
void performCreate(Bundle savedInstanceState) {
mChildFragmentManager.noteStateNotSaved();
mState = CREATED;
mCalled = false;
mSavedStateRegistryController.performRestore(savedInstanceState);
onCreate(savedInstanceState);
mIsCreated = true;
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onCreate()");
}
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
tips
遇到fragment
时呀、activity
嵌套的情況张漂,Fragment
的ViewModel
聲明不要在onCreate
里!谨娜,會(huì)導(dǎo)致銷毀重建數(shù)據(jù)無效航攒,原因和ViewModel
的恢復(fù)順序有關(guān)。Fragment
放在的onCreateView
趴梢、onViewCreated
里親測(cè)可以
問題1:為什么ViewModel
可以在Activity漠畜、Fragment
里共享數(shù)據(jù)?
ViewModel
存儲(chǔ)在ViewModelStore
類里坞靶。由ViewModelStoreOwner
在Activity憔狞、Fragment
里提供。
我們創(chuàng)建ViewModel的時(shí)候彰阴,ViewModelStoreOwner
指向Activity的那份即可瘾敢。
ViewModelProvider(activity, SavedStateViewModelFactory(null, this)).get( AnswerViewModel::class.java)