The ViewModel class is a business logic or screen level state holder.
上面是官方给的定义,ViewModel 类是业务逻辑或屏幕级状态持有者。
一、业务逻辑持有者
在此之前,无论是MVC模式,还是MVP模式,在视图层,都会产生对业务逻辑层的依赖,从而导致出现内存泄漏的情况。那ViewModel又是如何处理业务逻辑层和视图层的依赖,从而避免了常见的内存泄漏的问题的呢?
依赖Lifecycle对宿主生命周期的感知,处理对Viewmodel的依赖
1.1、应用
NormalViewModel
package com.anniljing.viewmodelcorestudy;
import android.app.Application;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
public class NormalViewModel extends AndroidViewModel {
public NormalViewModel(@NonNull Application application) {
super(application);
}
public List<Integer> getdatas() {
List<Integer> data = new ArrayList<>();
for (int i = 0; i < 10; i++) {
data.add(i);
}
return data;
}
}
NormalActivity
package com.anniljing.viewmodelcorestudy;
import android.os.Bundle;
import com.anniljing.viewmodelcorestudy.databinding.ActivityNormalBinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
public class NormalActivity extends AppCompatActivity {
private ActivityNormalBinding mBinding;
private NormalViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityNormalBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
mViewModel = new ViewModelProvider(this).get(NormalViewModel.class);
}
}
ComponentActivity源码
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();
}
}
}
});
此处在Activity中Lifecycle的ON_DESTROY事件中,清除了ViewModel。
1.2、源码解析
1.2.1 创建ViewModelStore
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
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();
}
}
}
});
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
ensureViewModelStore();
getLifecycle().removeObserver(this);
}
});
}
在ComponentActivity的构造方法中,注册了多个Lifecycle的观察者,其中有两个涉及ViewModelStore的,一个是收到宿主的生命周期事件后,就创建了ViewModelStore,随后就移除了该观察者;另一个则是监听到宿主的ON_DESTROY事件后,清空ViewModelStore的ViewModel
1.2.2、创建Factory
1.2.2.1、创建ViewModel的时候,第一步我们创建了ViewModelProvider,调用了其中一个构造方法
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
1.2.2.2、ViewModelProvider的构造方法中,调用了默认的Factory
public companion object {
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
}
1.2.2.3、由于ComponentActivity实现了HasDefaultViewModelProviderFactory接口,所以Factory的实际对象是SavedStateViewModelFactory
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
1.2.3、创建ViewModel
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
val extras = MutableCreationExtras(defaultCreationExtras)
extras[VIEW_MODEL_KEY] = key
// AGP has some desugaring issues associated with compileOnly dependencies so we need to
// fall back to the other create method to keep from crashing.
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) }
}
应用层通过ViewModelProvider的get方法创建ViewModel,并且保存到ViewModelStore中。
二、屏幕状态的持有者
该功能主要处理当设备配置发生变化时,界面会重新绘制,导致数据丢失的问题。
屏幕发生旋转的时候,会调用onDestroy方法,界面会重新绘制。
2.1、ViewModel之前
2.1.1、manifest中配置
在activity里面的configChanges属性里面配置orientation|screenSize,则屏幕旋转的时候,界面就不会重新绘制。
2.1.2、onSaveInstanceState和onRestoreInstanceState
如果manifest里面没有配置configChanges,则屏幕旋转的时候,在onDestroy之前会调用onSaveInstanceState,在恢复界面的时候,会调用onRestoreInstanceState,这样我们就可以在以上两个方法中做保存和恢复数据的逻辑。
package com.anniljing.viewmodelcorestudy;
import android.os.Bundle;
import android.util.Log;
import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class SaveStateActivity extends AppCompatActivity {
private static final String TAG = "SaveStateActivity";
private ActivitySaveStateBinding mBinding;
private NormalState mNormalState;
private static final String KEY="NormalState";
private String saveString="";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate");
if (savedInstanceState !=null){
saveString=savedInstanceState.getString(KEY,"");
}
mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
mNormalState = new NormalState();
mNormalState.setString(saveString);
Log.e(TAG, mNormalState.getString());
mBinding.tvState.setOnClickListener((view) -> {
mNormalState.setString("NormalState");
Log.e(TAG,"Click after:"+ mNormalState.getString());
});
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.e(TAG, "onSaveInstanceState");
outState.putString(KEY,"NormalState");
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e(TAG, "onRestoreInstanceState");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
}
保存数据:
重新绘制的时候,取出数据:
在onSaveInstanceState里面增加保存逻辑,在onCreate方法里面就可以恢复以前的数据了。
2.2、 使用ViewModel
ViewModelSaveState:
package com.anniljing.viewmodelcorestudy;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
public class ViewModelSaveState extends AndroidViewModel {
private String mString;
public ViewModelSaveState(@NonNull Application application) {
super(application);
}
public String getString() {
return mString;
}
public void setString(String string) {
mString = string;
}
}
package com.anniljing.viewmodelcorestudy;
import android.os.Bundle;
import android.util.Log;
import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
public class SaveStateActivity extends AppCompatActivity {
private static final String TAG = "SaveStateActivity";
private ActivitySaveStateBinding mBinding;
private ViewModelSaveState mViewModelSaveState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate");
mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
mViewModelSaveState = new ViewModelProvider(this).get(ViewModelSaveState.class);
Log.e(TAG,"Before click:"+mViewModelSaveState.getString());
mBinding.tvState.setOnClickListener((view) -> {
mViewModelSaveState.setString("ViewModelState");
Log.e(TAG, "Click after:" + mViewModelSaveState.getString());
});
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG,"onDestroy");
}
}
可以看到,引入ViewModel以后,不需要在onSaveInstanceState方法中增加额外的保存逻辑,重绘后就可以恢复以前的数据。
无论是ViewModel,还是重写onSaveInstanceState方法,当内存有限导致界面退出重绘时,就无法恢复以前的数据了。
我们可以通过以下操作来模拟内存不足时,导致应用杀死的效果:
1、启用开发者模式
2、打开“不保留活动”
3、打开应用后,按home键,退出应用
4、重新打开应用。
2.3、使用SavedStateHandle
ViewModelWithHandleSaveState
package com.anniljing.viewmodelcorestudy;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
public class ViewModelWithHandleSaveState extends ViewModel {
private static final String KEY = "ViewModelWithHandleSaveState";
private SavedStateHandle mStateHandle;
public ViewModelWithHandleSaveState(SavedStateHandle stateHandle) {
mStateHandle = stateHandle;
}
public MutableLiveData<String> getLiveData() {
return mStateHandle.getLiveData(KEY);
}
}
1、构造方法里面添加SavedStateHandle参数。
2、获取的时候通过mStateHandle。
SaveStateActivity
package com.anniljing.viewmodelcorestudy;
import android.os.Bundle;
import android.util.Log;
import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
public class SaveStateActivity extends AppCompatActivity {
private static final String TAG = "SaveStateActivity";
private ActivitySaveStateBinding mBinding;
private ViewModelWithHandleSaveState mWithHandleSaveState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate");
mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
mWithHandleSaveState = new ViewModelProvider(this).get(ViewModelWithHandleSaveState.class);
Log.e(TAG, "Before click:" + mWithHandleSaveState.getLiveData().getValue());
mWithHandleSaveState.getLiveData().observe(this, s -> {
Log.e(TAG, "After click:" + mWithHandleSaveState.getLiveData().getValue());
});
mBinding.tvState.setOnClickListener((view) -> mWithHandleSaveState.getLiveData().postValue("ViewModelWithHandleSaveState"));
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
}