前言
ViewModel旨在管理Activity和Fragment中的用户界面的数据,还可以用来这两者之间的通讯。
与ViewModel相关联的Activity或者Fragment,该两者只要处于活动状态ViewModel就不会被销毁。
(这里需要配合LiveData使用),综合了网上的内容加上自己整理而出的有错误请帮忙指出谢谢了。
引用ViewModel
导入androidx替换掉support包。
简单使用:
/*
新建User类
*/
public class User {
public String name;
public int age;
public User(String name, int age){
this.name = name;
this.age = age;
}
@NonNull
@Override
public String toString() {
return "UserInformation:"+"\n"
+ name + "\n"
+age ;
}
}
/*
继承ViewModel
*/
public class UseViewModel extends ViewModel {
public final MutableLiveData<User> data = new MutableLiveData<>();
public UseViewModel(){
//模拟加载数据
data.postValue(new User(" 张三",20));
}
// 数据操作
public void doSomething(){
User user = data.getValue();
if(user != null) {
user.name = "李四";
user.age = 25;
data.setValue(user);
}
}
}
/*
在Activity中创建实例
*/
public class MainActivity extends FragmentActivity {
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.view_model);
button = findViewById(R.id.view_button);
//实例化
final UseViewModel viewModel = ViewModelProviders.of(this).get(UseViewModel.class);
viewModel.data.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
textView.setText(user.toString());
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.doSomething();//改变数据
}
});
}
}
这时候点击一下按钮,TextView里的数据就从原来的:张三 20,变成李四25。然后我们在旋转屏幕就会发现原来的数据还存在没有发生改变,这就是ViewModel的厉害的地方,这就是不的不提到ViewModel的生命周期了。
- Activity与Fragment通信
有了ViewModel Activity就能和Fragment共用一个ViewModel,从而使他们互相通信,然后接着上面的继续:
public class Fragment extends androidx.fragment.app.Fragment {
private TextView fragmentText;
private Button fragmentButton;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment,container,false);
//Fragment中的两个控件
fragmentText = view.findViewById(R.id.fragment_view);
fragmentButton = view.findViewById(R.id.fragment_button);
//获取实例
final UseViewModel model = ViewModelProviders.of(this).get(UseViewModel.class);
model.data.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
//TextView 监控 数值的变化
fragmentText.setText(user.toString());
}
});
/*
改变值
*/
fragmentButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
model.doSomething();
}
});
return view;
}
}
点击按钮就会发现Fragment中的数据跟Activity中的数据一样了,并且旋转屏幕数据依然不会消失。至于Activity之间的通信实现方式跟上面差不多。
ViewModel源码
//从这一句开始
UseViewModel viewModel = ViewModelProviders.of(this).get(UseViewModel.class);
首先看 ViewModelProviders.of(this)方法
//在Fragment中获取ViewModel实例的
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
of()方法就是为了在不同布局中构建ViewModelProvider,而ViewModelProvider就是提供ViewModel的。
Factory 是ViewProvider的内部的一个接口,它有两个实现类 NewInstanceFactory和 AndroidViewModelFactory 。
NewInstanceFactory的源码
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
NewInstanceFactory是用来实例化那种没有参数的class,并且通过newInstance()方法获取实例的。
AndroidViewModelFactory源码
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
AndroidViewModelFactory是用来实例化构造方法中可能会有参数的class,并且可能具有Context的,通过newInstance(mApplication)实例化,如果没有就还是用newInstance()方法获取实例 从return super.create(modelClass);这里可以看出来。
现在来看new ViewModelProvider(activity.getViewModelStore(), factory);,第一个参数会调用FragmentActvity的getViewModelStore()方法,其返回类型为ViewModelStore,这个类是用来存储ViewModel的。
- getViewModelStore()的源码:
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) {//如果没有就创建一个新的ViewModelStore
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();
重点是这一句,那么NonConfigurationInstances是什么呢?继续往下看
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
这个是FragmentActivity的一个嵌套类,保存ViewModelStore的实例。
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
//获取最近一次横竖屏切换时保存下来的数据。
继续看getLastNonConfigurationInstance()方法,
//这是Activity.class里的方法
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
//返回任意类型任意可以是fragment也可以是activity
我们继续深入getLastNonConfigurationInstance() ,该方法出现在onCreat方法内:
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
. . . . . .
}
原来在activity调用onCreate方法时就保存了ViewModelStore的实例了,然后放入到NonConfigurationInstances里面,再次调用onCreate()方法时则又恢复过来。从而使横竖屏切换的时候数据不会丢失。
ViewModelProviders.of(this).get(UseViewModel.class),中的get()方法
public <T extends ViewModel> T get( Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
//这里我们会发现如果直接使用new ViewModel()创建实例就会直接报错。
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
...
public <T extends ViewModel> T get( String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
//先取缓存
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//没有缓存就创建然后put起来
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
总体上还是很清晰的,有缓存就用缓存,没缓存就重新构建。
ViewModelStore
上面很多代码中都有这个,它其实是一个很简单的保存通过HaspMap来保存ViewModel的类
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
//通过key缓存ViewModel
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
//通过key获取实例
final ViewModel get(String key) {
return mMap.get(key);
}
/**
* 清除内部储存,并不再使用ViewModel
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
那么什么时候调用clear()清除ViewModel呢?继续深入Clear()发现在FragmentActivity中的onDestory()方法中出现。
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {//isChangingConfigurations()直接返回false
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
不足之处还请不吝提出谢谢。