关于ViewModel 介绍,文章不过多阐述。官方文档
本文将从三个方面做一定阐述
为什么需要ViewModel
-
你需要处理配置变化
- 笔者认为,用户可以随时改变配置(i.e 旋转屏幕,切换语言,切换系统文字大小 ...),这可能会导致当前Activity重建,这些都不受开发者的控制,但是你又不得不处理它
-
可能很多APP在配置清单文件中申明了每一个Activity
orientation = portrait,但是你无法禁止用户去改变语言、文字大小。这样就可能会导致Activity被移除或者重新创建
-
为什么
onSaveInstanceState依旧不够传统的做法都是在配置发生变化即
onSaveInstanceState方法去save data, 在onCreate去restore data但是这里有两个限制
-
onSaveInstanceState方法不能够缓存较大的数据,笔者之前尝试缓存上百兆数据发现抛出了TransactionTooLargeException -
保存的数据一定需要实现
serializable或者Parceable, 但是有时候这些数据来自第三方库,我们不能修改它,对于某些场景,很难在onSaveonSaveInstanceState中保存数据
-
基于上述两点,ViewModel应运而生
- 配置改变前后数据存储与恢复
一些例子
基础功能
public class ZeroViewModel extends ViewModel {
public User user;
}
public class ZeroDemo extends AppCompatActivity {
private TextView tv;
private ZeroViewModel vm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tv_btn);
tv = findViewById(R.id.tv_simple);
vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
System.out.println("szw vm.user = " + vm.user);
}
// android:onClick="onClickSimpleButton"
public void onClickSimpleButton(View v) {
vm.user = new User(23, "jorden");
}
}
旋转屏幕,vm.user 依旧 != null
同一个Activity不同实例
- 同时存在两个实例
public class SameClass01 extends AppCompatActivity {
private TextView tv;
private ZeroViewModel vm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tv_btn);
tv = findViewById(R.id.tv_simple);
vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
System.out.println("szw SameClass01 : " + vm.user);
}
// launch the second instance
// android:onClick="onClickSimpleButton"
public void onClickSimpleButton(View v) {
vm.user = new User(100, "SuperMario");
startActivity(new Intent(this, SameClass01.class));
}
}
即使有两个同类的Activity实例,第一个vm.user 持有的依旧是Mario,第二个vm.user 持有null
- finish再重新创建
public class SameClass02 extends AppCompatActivity {
private TextView tv;
private ZeroViewModel vm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tv_btn);
tv = findViewById(R.id.tv_simple);
vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
System.out.println("szw SameClass02 onCreate() : " + vm.user);
}
// android:onClick="onClickSimpleButton"
public void onClickSimpleButton(View v) {
vm.user = new User(22, "test");
}
// android:onClick="onClickSimpleButton2"
public void onClickSimpleButton2(View v) {
System.out.println("szw SameClass02 : saved = "+vm.user);
}
}
先启动SameClass02 ,执行onClickSimpleButton,finish重新打开,日志输出null
上述两个例子表现正常
和Static申明的变量比较
- 基础比较
public class SameVm {
public static User user;
}
public class ZeroDemo extends AppCompatActivity {
private TextView tv;
private ZeroViewModel vm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tv_btn);
tv = findViewById(R.id.tv_simple);
String value = savedInstanceState == null ? "emptyBundle" : savedInstanceState.getString("key");
System.out.println("szw onCreate() " + value);
vm = ViewModelProviders.of(this).get(ZeroViewModel.class);
System.out.println("szw vm.user = " + vm.user);
System.out.println("szw static = "+SameVm.user);
}
// android:onClick="onClickSimpleButton"
public void onClickSimpleButton(View v) {
vm.user = new User(23, "jorden");
SameVm.user = new User(21, "king");
}
}
旋转屏幕后,日志输出
szw vm.user = User{id=23, name='jorden'}
szw static = User{id=21, name='king’}
-
终止应用
Terminate Application
和1同样的操作
szw vm.user = null szw static = null
从上面两个例子,感觉没什么不同。他们都能缓存数据,终止应用程序都会被销毁
它们的不同之处:
- ViewModel 主要是为了解耦,有点类似于MVP中的P,ViewModel 是MvvM中VM。你能在ViewModel中做异步操作(i.e访问网络),你可以改变data并且让View接受到通知LiveData
- static value 能被任何类修改,但是ViewModel 是Activity的私有变量,有点类似ThreadLocal
- ViewModel 可以判断Activity是正常销毁或者配置改变,进而做出不同的响应,finish->removedata ,configurationchange->savedata,静态变量却不能
相关注意事项
- ViewModel不要应用Activity等相关实例,容易造成内存泄漏
- 如果你需要在ViewMolde中获取Resource LocationManager等系统服务,可以继承AndroidViewModel
- ViewModel本身不支持事件模型(EventBus),你可以使用LiveData,当然为了解决旋转屏幕后,再次注册Observer,重复提示,可以使用 SingleLiveEvent
源码如下
public class DupliViewModel extends ViewModel {
private SingleLiveEvent<String> message = new SingleLiveEvent<>();
public void fetchMessage(){
message.setValue("A New Value");
}
public LiveData<String> getMessage() {
return message;
}
}
public class DupliObserverDemo extends AppCompatActivity {
private TextView tv;
private DupliObserverDemo self;
private DupliViewModel vm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tv_btn);
self = this;
tv = findViewById(R.id.tv_simple);
vm = ViewModelProviders.of(this).get(DupliViewModel.class);
vm.getMessage().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
System.out.println("szw updated ~");
Toast.makeText(self, "updated "+s, Toast.LENGTH_SHORT).show();
}
});
}
// android:onClick="onClickSimpleButton"
public void onClickSimpleButton(View v) {
vm.fetchMessage();
}
}
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。