关于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(); } }
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。