一、ViewModel
ViewModel 概览 | Android 开发者 | Android Developers
注意:ViewModel
绝不能引用视图、Lifecycle
或可能存储对 Activity 上下文的引用的任何类。
1. ViewModel其实只是一个抽象类class,只有一个onCleared函数,没有其他。
很多人说他能感知生命周期是不严谨的;如果你直接new XXViewModel(),是没有生命感知能力的。
所以我建议标注一下deprecated,提醒你不要手动new,应该用接下来的第二点来新建出来。但是注意,不能改成private,否则无法框架无法newInstance新建对象。
/**
* @deprecated 不建议自己new
*/
public DataViewModel() {}
2. 你必须使用ViewModelProvider得到XXXViewModel
你必须使用,ViewmodelProviders.of(activity/fragment).get(xxxViewModel.class)来获取。
或者新版库,通过new ViewModelProvider(activity/fragment).get(xxxViewModel.class)。
为什么呢?接着看第三点。
3. XXXViewModel在Activity或Fragment里的唯一性
从ViewModelProvider的构造函数就可以看出,我们传递的owner是Activity|Fragment 本身;
//没人会用这个,这样的话,就要自己管理Store。
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
//都是传入Fragment|Activity
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
其实是support库的版本,他们在框架中实现了可以获取到getViewModelStore();比如android.support.v4.app.Fragment :
public ViewModelStore getViewModelStore() {
if (getContext() == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
保证了一个Activity|Fragment只有一份ViewModelStore。比如activity我们在各种fragment里面getActivity.getViewModel()就是这一份了。
接着,从ViewModelProvider.get方法,获取的时候,如下代码:
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
总结:
一个activity或者fragment,只有一个ViewModelStore 里面用hashMap存储k(xxxViewModel.class), v (框架反射new出来的xxViewModel对象)
保证了XXXViewModel在Activity或Fragment里的唯一性。
4. 生命周期
-
新建: 很好理解,就是前面我们通过ViewModelProvider新建出来的。
-
移除:
从google的原图,我们知道,ViewModel的生命周期比Activity基本相同(除了configuration改变时候长一些),在destory的时候,触发onCleared。//Fragment | FragmentActivity getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { // Clear out the available context mContextAwareHelper.clearAvailableContext(); // And clear the ViewModelStore if (!isChangingConfigurations()) { //提示我们什么?! getViewModelStore().clear(); } } } }); //fragmentStateManager boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack(); boolean shouldDestroy = beingRemoved || mFragmentStore.getNonConfig().shouldDestroy(mFragment); if (shouldDestroy) { FragmentHostCallback<?> host = mFragment.mHost; boolean shouldClear; if (host instanceof ViewModelStoreOwner) { shouldClear = mFragmentStore.getNonConfig().isCleared(); //反复出现。 } else if (host.getContext() instanceof Activity) { Activity activity = (Activity) host.getContext(); shouldClear = !activity.isChangingConfigurations(); } else { shouldClear = true; } if (beingRemoved || shouldClear) { mFragmentStore.getNonConfig().clearNonConfigState(mFragment); } //ViewModelStore.java public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); }
上述代码中反复出现了,判断当前是否是变化了config,引起的activity的重建。
再跑去framework里面看源码,
Called by the system, as part of destroying an
activity due to a configuration change, when it is known that a new
instance will immediately be created for the new configuration. You
can return any object you like here, including the activity instance
itself, which can later be retrieved by calling
{@link #getLastNonConfigurationInstance()} in the new activity
instance.
public Object onRetainNonConfigurationInstance() {}
这个函数也提示了如下。
而ViewModel的重新获取正是因为,
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
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;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
handleRelaunchActivity(r);
...
handleDestroyActivity(r.token, false, configChanges, true); //最后一位true
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
...
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances(); //here
//onRetainNonConfigurationInstance
小结:
一般情况,是懒加载,viewModel不用则是空的。
当我们刚使用才初始化ViewModelStore和我们的ViewModel。
利用android framework在改变configarition(比如方向),会restartActivity,这个时候会通过
回调activity的onRetainNonConfigurationInstance方法,你可以传递一些残留参数。
如果不是这种restart,则就正常清理了getViewModelStore().clear();
二、LiveData
怎么看,怎么觉得LiveData就是javafx中的ObjectProperty, javafx中的组件如ListView,Button等里面的所有参数全部是XXXProperty,就是为了绑定以后,数据更新监听变化直接更新UI。
研究后,我认为从LiveData的使用角度来把握原理更容易理解。请参考原文LiveData 概览 | Android 开发者 | Android Developers。
他的使用场景主要有如下3种:
2.1 结合ViewModel中使用
为什么官方建议LiveData建议放在ViewModel中去使用。因为,Activity在切换configuration状态的时候,ViewModel能保证存活,继而LiveData才能存活。否则你放在activity中,不论在哪里new LiveData,他的生命周期就不能脱离activity。这就是前面我讲的ViewModel章节的第四点。很多人没有讲清楚这一点根本原因。
如官方代码所示:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData<String> currentName;
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
// Rest of the ViewModel...
}
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
model = new ViewModelProvider(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}
//调用
model.getCurrentName().setValue(anotherName);
我们关注observe(this, observer)这个方法.
Adds the given observer to the observers list within the lifespan of the given owner. The events are dispatched on the main thread. If LiveData already has data set, it will be delivered to the observer.
The observer will only receive events if the owner is in Lifecycle.State.STARTED or Lifecycle.State.RESUMED state (active).
If the owner moves to the Lifecycle.State.DESTROYED state, the observer will automatically be removed.
When data changes while the owner is not active, it will not receive any updates. If it becomes active again, it will receive the last available data automatically.
LiveData keeps a strong reference to the observer and the owner as long as the given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to the observer & the owner.
If the given owner is already in Lifecycle.State.DESTROYED state, LiveData ignores the call.
If the given owner, observer tuple is already in the list, the call is ignored. If the observer is already in the list with another owner, LiveData throws an IllegalArgumentException.
深刻理解英文doc十分重要。
传入的第一个参数owner,是为了感知这个参数的生命周期。第二个参数是回调,内部会帮你切换到主线程。
2.2 extends它使用
官方文档扩展LiveData有展示;但没人监听我们的LiveData就可以不再做请求;但有人连接进入我们就request一下。(可能我会直接在onCreate里面调用数据请求吧,而不是通过这个onActive的回调来处理 )。目的是为了onActive的时候,直接作出请求动作,达到解耦。
2.3 单例
您可以使用单例模式扩展 LiveData
对象以封装系统服务,以便在应用中共享它们。LiveData
对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData
对象。如需了解详情,请参阅扩展 LiveData
但是有一个注意点,后面会提到。data不宜是大型数据。
2.4 简述原理
- 如何感知生命周期
owner.getLifecycle().addObserver(wrapper); 如上面代码,就自动注册了监听;
//LiveData的方法
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//...
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //新建一个包装类型
//....
owner.getLifecycle().addObserver(wrapper);//注册监听从activity处得到回调触发下面的函数
}
//注册了监听后的回调
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver); //而当生命周期结束后,自动反注册。
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive()); //一系列判断;最终触发onChanged让我们app开发知道
currentState = mOwner.getLifecycle().getCurrentState();
}
}
查看mObservers的调用逻辑,只有当主动removeObserver的时候他会进行移除;当生命周期到了DESTRORY他也会自动反注册。
- 防止内存泄漏
//LiveData的成员变量
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
而SafeIterableMap是链表作为数据结构,链表并不是弱引用,只是它的迭代器使用弱引用来实现,避免迭代导致的引用加多,这里容易误会。不过问题不大,我们只要搞清楚LiveData持有一个mObservers的链表,GC存活时间与之相同即可。
//这是我们在Activity中的申明;
Observer<String> nameObserver = new Observer<String>() {
onChanged(xxx)
}
value是:
//LiveData的方法
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//....
owner.getLifecycle().addObserver(wrapper);
}
wrapper内部持有了owner,即Activity|Fragmnet本身;而observer则作为往外通知的接口,调用一下onChanged。
因此,当Activity或者Fragment被系统回收,对于第一种用法的LiveData只是Activity的成员变量或者是ViewModel的成员变量,都会随着Activity的Destory而回收(configuration变化除外)。这也是ViewModel不得引用context的原因。
那么,即使是第2,3种单例使用LiveData,由于Activity的DESTROYED state出现会自动解除注册,也不会导致引用Activity,不用担心内存泄漏。但是,你的real data却并不能释放,就不太建议使用LiveData存储复杂类型,尤其有人new Fragment给一个单例中的LiveData去存着,这就导致这个Fragment一直存在。
到这里,我们就知道viewModel的基本逻辑了。所以,为什么android大力推进jetpack,默认使用appCompactActivity,最重要的一点是,有人基于框架给你做了一个基础建设。框架搭好,在这个基础上才能玩出花活。比如你的Activity直接继承android.app.Activity,就没有lifecycle,viewmodelStore这些,自然就没有那么多生命感知的东西了。