Jetpack 之 ViewModel

ViewModel 以注重生命周期的方式存储和管理界面相关的数据。
ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。


问题:

1.旋转界面,配置更改,导致数据丢失,onSaveInstanceState() 方法保存 然后从 onCreate() 中的Bundle 或者 onRestoreInstanceState 恢复数据,但IPC对Bundle有1M的限制,如何做到大数据恢复

2.逻辑层会持有UI层的引用(比如:在MVP的Presenter中需要持有IView接口来回调结果给界面),如何避免内存泄漏


ViewModel生命周期长于Activity,因系统配置变更Activity销毁重建,ViewModel对象会保留并关联到新的Activity。而Activity的正常销毁(系统不会重建Activity)时,ViewModel对象是会清除的。

ViewModel包装了基于观察者模式的LiveData(问题2解决)


ViewModel使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class TestViewModelActivity : AppCompatActivity() {

val TAG = "TestViewModelActivity"
val myViewModel: MyViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_viewmodel)
val name = findViewById<TextView>(R.id.name)

Log.d(TAG, "myViewModel: $myViewModel")
Log.d(TAG, "onCreate: / " + System.currentTimeMillis())

myViewModel.getUserData().observe(this, {
Log.d(TAG, "observe:" + it.toString() + " / " + System.currentTimeMillis())
name.text = it.toString()
})
}

override fun onStart() {
super.onStart()
Log.d(TAG, "onStart: ")
}

override fun onResume() {
super.onResume()
Log.d(TAG, "onResume: ")
}

override fun onPause() {
super.onPause()
Log.d(TAG, "onPause: ")
}

override fun onStop() {
super.onStop()
Log.d(TAG, "onStop: ")
}

override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: ")
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyViewModel : ViewModel() {

private val userData: MutableLiveData<List<String>> by lazy {
MutableLiveData<List<String>>().also {
getData()
}
}

private fun getData() {
Thread {
Thread.sleep(3000)
userData.postValue(listOf("Stew", "Helen", "Bob", "Lucy"))
Thread.sleep(3000)
userData.postValue(listOf("Stew", "Helen", "Bob"))
}.start()
}

fun getUserData(): LiveData<List<String>> {
return userData
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TestViewModelActivity: myViewModel: com.stew.kotlinjetpack.viewmodel.MyViewModel@7ee52e8
TestViewModelActivity: onCreate: / 1658413519213
TestViewModelActivity: onStart:
TestViewModelActivity: onResume:
TestViewModelActivity: observe:[Stew, Helen, Bob, Lucy] / 1658413522218
TestViewModelActivity: observe:[Stew, Helen, Bob] / 1658413525219

旋转屏幕

TestViewModelActivity: onPause:
TestViewModelActivity: onStop:
TestViewModelActivity: onDestroy:
TestViewModelActivity: myViewModel: com.stew.kotlinjetpack.viewmodel.MyViewModel@7ee52e8
TestViewModelActivity: onCreate: / 1658413576399
TestViewModelActivity: onStart:
TestViewModelActivity: observe:[Stew, Helen, Bob] / 1658413576410
TestViewModelActivity: onResume:

如果在初始化的时候就开始获取数据,必须在子线程异步获取,不然会报错(?????)

旋转屏幕myViewModel不会变化,退出重进会变化


fragment共享viewmodel数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Test4Fragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<TextView>(R.id.t1).setOnClickListener {
i++
sharedViewModel.getData(i.toString())
}
}
}

class Test5Fragment : Fragment() {
val sharedViewModel: SharedViewModel by activityViewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val t = view.findViewById<TextView>(R.id.t1)
sharedViewModel.getSharedData().observe(viewLifecycleOwner, {
t.text = "Test5 data: $it"
})
}

}

源码分析

ViewModel实例的获取是通过ViewModelProvider类

ViewModelStoreOwner——ViewModel存储器拥有者;
ViewModelStore——ViewModel存储器,用来存ViewModel的地方;
Factory——创建ViewModel实例的工厂(通过传入的class 反射获取ViewModel实例。)

ViewModelStoreOwner是个接口,实现类有Activity/Fragment,也就是说 Activity/Fragment 都是 ViewModel存储器的拥有者

ViewModelStore中,viewModel作为Value存储在HashMap中。

viewModelProvider.get(UserViewModel.class)
先尝试从ViewModelStore获取ViewModel实例,如果没有获取到,就使用Factory创建,然后存入ViewModelStore。

ViewModelStoreOwner是个接口,实现类有Activity/Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}

先尝试 从NonConfigurationInstance从获取 ViewModelStore实例,如果NonConfigurationInstance不存在,就new一个mViewModelStore。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();

ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}

if (viewModelStore == null && custom == null) {
return null;
}

NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}

并且还注意到,在onRetainNonConfigurationInstance()方法中 会把mViewModelStore赋值给NonConfigurationInstances,在Activity因配置改变 而正要销毁时,且新Activity会立即创建,那么系统就会调用此方法,也就说,配置改变时 系统把viewModelStore存在了NonConfigurationInstances中。

ComponentActivity静态内部类,非配置实例

1
2
3
4
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Acticity.java

NonConfigurationInstances mLastNonConfigurationInstances;

//返回onRetainNonConfigurationInstance()返回的实例
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
}

static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}


final void attach(Context context, ActivityThread aThread, ...
NonConfigurationInstances lastNonConfigurationInstances,... ) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}

mLastNonConfigurationInstances是在Activity的attach方法中赋值

lastNonConfigurationInstances是存在 ActivityClientRecord中的一个组件信息

ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影响,那么ActivityClientRecord中lastNonConfigurationInstances也不受影响,那么其中的Object activity也不受影响,那么ComponentActivity中的NonConfigurationInstances的viewModelStore不受影响,那么viewModel也就不受影响了。(问题1解决)


  • onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用

  • 使用ViewModel恢复数据 则 只有在 因配置更改界面销毁重建 的情况。

  • ViewModel是存在内存中,读写速度快

  • onSaveInstanceState是序列化到磁盘中

  • ViewModel,可以存复杂数据,大小限制就是App的可用内存

  • onSaveInstanceState只能存可序列化和反序列化的对象,且大小有限制(一般Bundle限制大小1M)。