使用 Android 视图模型
Using Android ViewModel
当我使用 ViewModel
测试一个小代码时,我注意到一个逻辑上正确的小问题,我想知道你是如何解决这个问题的。来看看这个小class:
public class MyDataViewModel extends ViewModel {
MutableLiveData<List<MyData>> mData = new MutableLiveData<>();
public ContactsViewModel() {}
public void setData(List<MyData> data) {
mData.postValue(data);
}
public LiveData<List<MyData>> getData() {
return mData;
}
}
问题是,如果您在 LiveData
观察者注册之前使用 setData()
更改 LiveData
,您的观察者 注册观察者后不会触发。尽管这看起来合乎逻辑,但是当您编写异步代码时可能会导致问题,并且您不知道 setData()
是否会在注册观察者之前被调用。我想知道当你想注册观察者时你如何检查数据是否已经设置。只需检查 getData().getValue() != null
?
另一个问题是数据同步。我是否需要牢记 LiveData
同步(就像所有其他正常数据一样),或者 LiveData
内部处理它?例如,可以同时调用 setData()
和 getData().getValue()
吗?
最后一个问题,似乎 LiveData
观察者会在你设置一个值的任何时候触发,即使它是相同的(例如,如果你在 onLoadFinished()
中使用 setData()
每次 activity 重新创建时都会调用 Loader
、setData()
)。这将导致观察者使用相同的数据调用两次。我想知道什么是防止这种情况的最佳方法。检查ViewModel
中的数据是否与我们现有的相似,不要重新设置值?
Problem is that if you use setData() to change LiveData before LiveData observer is registered, your observer will not trigger after registering observer
根据 LiveData 中 observe()
方法的文档:
/**
* Adds the given observer to the observers list within the lifespan of the given
* owner. The events are dispatched on the main thread.
* If LiveData already has data set, it will be delivered to the observer.
所以如果LiveData已经有数据集,它会被传递给观察者。如果没有,那就是一个错误。
Another question is data synchronization. Do I need to keep LiveData synchronization in mind (like all other normal data), or LiveData internally handles it?
当通过 setValue()
(仅主线程)或 postValue()
方法修改 LiveData 中的数据时,LiveData 会通知所有订阅者。
为了使 LiveData 保持最新,something 必须使用最新数据更新其值,以便它 "broadcast" 将其发送给所有订阅者。
例如,当您从 Room 的 DAO 提供 LiveData<T> getData();
时,Room 会检查 table for T
是否被修改。如果它被修改,那么它将更新所有使用 table 的 LiveData
for T
.
您几乎不需要致电 getValue()
。
This will cause observer called twice with same data. I wonder what is best way to prevent this.
如果您正确设置了 diffing 或您的观察逻辑,这应该无关紧要。
public class TasksViewModel
extends BaseObservable
implements Observer<List<Task>> {
@Inject
TasksViewModel(...) {
//...
}
private LiveData<Task> liveResults;
public void start() {
liveResults = tasksRepository.getTasks();
liveResults.observeForever(this);
}
public void stop() {
liveResults.removeObserver(this);
}
@Override
public void onChanged(@Nullable List<Task> tasks) {
if(tasks == null) {
return; // loading...
}
items.clear();
items.addAll(tasks); // <-- whether it is same data or not is irrelevant.
notifyPropertyChanged(BR.empty);
}
}
对于 Android 的 ViewModel
,您很可能会将 start()
移至构造函数(使用 ViewModelProvider.Factory
),并将 stop()
移至 onCleared()
.
如果有人需要 LiveData
,它只在 setData()
上调用观察者,我写了这个小 class。当您决定在 onLoaderFinished()
上 setData()
并重新创建 activity 时,我自己使用它来防止多个观察者调用。
public class MutableSemiLiveData<T> extends MutableLiveData<T> {
private volatile boolean mManualSetting = false;
@Override
protected void onInactive() {
super.onInactive();
mManualSetting = false;
}
@Override
public void setValue(T value) {
mManualSetting = true;
super.setValue(value);
}
@Override
public void postValue(T value) {
mManualSetting = true;
super.postValue(value);
}
@Override
public void observe(LifecycleOwner owner, final Observer<T> observer) {
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mManualSetting) {
observer.onChanged(t);
}
}
});
}
}
请记住,仅当您想通过设置值触发观察者时才使用此 class,并且它不会在 activity 重新创建时触发。
此致
当我使用 ViewModel
测试一个小代码时,我注意到一个逻辑上正确的小问题,我想知道你是如何解决这个问题的。来看看这个小class:
public class MyDataViewModel extends ViewModel {
MutableLiveData<List<MyData>> mData = new MutableLiveData<>();
public ContactsViewModel() {}
public void setData(List<MyData> data) {
mData.postValue(data);
}
public LiveData<List<MyData>> getData() {
return mData;
}
}
问题是,如果您在 LiveData
观察者注册之前使用 setData()
更改 LiveData
,您的观察者 注册观察者后不会触发。尽管这看起来合乎逻辑,但是当您编写异步代码时可能会导致问题,并且您不知道 setData()
是否会在注册观察者之前被调用。我想知道当你想注册观察者时你如何检查数据是否已经设置。只需检查 getData().getValue() != null
?
另一个问题是数据同步。我是否需要牢记 LiveData
同步(就像所有其他正常数据一样),或者 LiveData
内部处理它?例如,可以同时调用 setData()
和 getData().getValue()
吗?
最后一个问题,似乎 LiveData
观察者会在你设置一个值的任何时候触发,即使它是相同的(例如,如果你在 onLoadFinished()
中使用 setData()
每次 activity 重新创建时都会调用 Loader
、setData()
)。这将导致观察者使用相同的数据调用两次。我想知道什么是防止这种情况的最佳方法。检查ViewModel
中的数据是否与我们现有的相似,不要重新设置值?
Problem is that if you use setData() to change LiveData before LiveData observer is registered, your observer will not trigger after registering observer
根据 LiveData 中 observe()
方法的文档:
/** * Adds the given observer to the observers list within the lifespan of the given * owner. The events are dispatched on the main thread. * If LiveData already has data set, it will be delivered to the observer.
所以如果LiveData已经有数据集,它会被传递给观察者。如果没有,那就是一个错误。
Another question is data synchronization. Do I need to keep LiveData synchronization in mind (like all other normal data), or LiveData internally handles it?
当通过 setValue()
(仅主线程)或 postValue()
方法修改 LiveData 中的数据时,LiveData 会通知所有订阅者。
为了使 LiveData 保持最新,something 必须使用最新数据更新其值,以便它 "broadcast" 将其发送给所有订阅者。
例如,当您从 Room 的 DAO 提供 LiveData<T> getData();
时,Room 会检查 table for T
是否被修改。如果它被修改,那么它将更新所有使用 table 的 LiveData
for T
.
您几乎不需要致电 getValue()
。
This will cause observer called twice with same data. I wonder what is best way to prevent this.
如果您正确设置了 diffing 或您的观察逻辑,这应该无关紧要。
public class TasksViewModel
extends BaseObservable
implements Observer<List<Task>> {
@Inject
TasksViewModel(...) {
//...
}
private LiveData<Task> liveResults;
public void start() {
liveResults = tasksRepository.getTasks();
liveResults.observeForever(this);
}
public void stop() {
liveResults.removeObserver(this);
}
@Override
public void onChanged(@Nullable List<Task> tasks) {
if(tasks == null) {
return; // loading...
}
items.clear();
items.addAll(tasks); // <-- whether it is same data or not is irrelevant.
notifyPropertyChanged(BR.empty);
}
}
对于 Android 的 ViewModel
,您很可能会将 start()
移至构造函数(使用 ViewModelProvider.Factory
),并将 stop()
移至 onCleared()
.
如果有人需要 LiveData
,它只在 setData()
上调用观察者,我写了这个小 class。当您决定在 onLoaderFinished()
上 setData()
并重新创建 activity 时,我自己使用它来防止多个观察者调用。
public class MutableSemiLiveData<T> extends MutableLiveData<T> {
private volatile boolean mManualSetting = false;
@Override
protected void onInactive() {
super.onInactive();
mManualSetting = false;
}
@Override
public void setValue(T value) {
mManualSetting = true;
super.setValue(value);
}
@Override
public void postValue(T value) {
mManualSetting = true;
super.postValue(value);
}
@Override
public void observe(LifecycleOwner owner, final Observer<T> observer) {
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mManualSetting) {
observer.onChanged(t);
}
}
});
}
}
请记住,仅当您想通过设置值触发观察者时才使用此 class,并且它不会在 activity 重新创建时触发。
此致