关于View Model
官网都有介绍了哈,
https://developer.android.com/topic/libraries/architecture/viewmodel?hl=zh-cn
官网里面的生命周期图片,一目了然
总的来说是,就是保证数据在整个Activity 生命周期都不会丢失,包括旋转屏.
使用方式
使用也很简单
Activity中,套用官网的代码参考就可以了,这个很简单
public class DiceUiState { private final Integer firstDieValue; private final Integer secondDieValue; private final int numberOfRolls; // ... } public class DiceRollViewModel extends ViewModel { private final MutableLiveData<DiceUiState> uiState = new MutableLiveData(new DiceUiState(null, null, 0)); public LiveData<DiceUiState> getUiState() { return uiState; } public void rollDice() { Random random = new Random(); uiState.setValue( new DiceUiState( random.nextInt(7) + 1, random.nextInt(7) + 1, uiState.getValue().getNumberOfRolls() + 1 ) ); } }
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
model.getUiState().observe(this, uiState -> {
// update UI
});
}
}
照抄就可以运行了
对于fragment ,这个稍微有点需要注意的地方.
Activity 中数据有变化,在Fragment 中更新不了?或者一个Activity 中两个Fragment 互相交互数据没法更新到对方的ViewModel 中.有点绕,举个例子.
我写了一个简单的页面
一个页面里的两个Fragment, 通过button1 改变 Fragment2 中的值
button2 改变frament1 中的文本值,用view model 实现
放有问题的代码,逻辑比较简单
ViewModel
public class MyViewModel extends ViewModel {
protected MutableLiveData<String> test1 = new MutableLiveData<>();
protected MutableLiveData<String> test2 = new MutableLiveData<>();
public void setTest1(String name){
test1.setValue(name);
}
public void setTest2(String name){
test2.setValue(name);
}
}
BlankFragment1
button1.setOnClickListener(v -> {
model.setTest2("BlankFragment1");
});
model = new ViewModelProvider(this).get(MyViewModel.class);
model.test1.observe(this, tset -> {
Log.e("xss",tset);
// update UI
blank1.setText(tset);
});
BlankFragment2
button2.setOnClickListener(v -> {
model.setTest1("BlankFragment2");
});
model = new ViewModelProvider(this).get(MyViewModel.class);
model.test2.observe(this, tset -> {
// update UI
Log.e("xss",tset);
textView.setText(tset);
});
通过点击发现页面文本没有任何改变....然后我把model 对象打印出来,发现两个Frament 中压根不是一个对象,如何保证和Activity 中同样的对象呢.
model = new ViewModelProvider(this).get(MyViewModel.class); 错误
model = new ViewModelProvider(getActivity()).get(MyViewModel.class); 正确
修改后验证ok ,Fragment 之间,或者Fragment 和Actvity 之前数据都可以共享了
AndroidViewModel
我们使用ViewModel 的时候难免会需要一些context 对象,当然我们也可以通过参数的方式传递当前的context,但是这样可能会导致内存泄露,于是为了解决这个问题,google 为我们提供了AndroidViewModel.
可以看到接收的是Application 作为context,应用的生命周期一致,解决内存泄露的问题
public class MyAndroidViewModel extends AndroidViewModel {
public MyAndroidViewModel(@NonNull @NotNull Application application) {
super(application);
}
}
其他使用方式同ViewModel 一致
其他用法
postValue()
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
看接口描述就知道在其他线程中调用,和handler 中更新MainThread 中UI 效果一样.当然了如果是主线程的话,直接调用setValue() 即可
参考
https://developer.android.com/topic/libraries/architecture/viewmodel?hl=zh-cn