【问题标题】:Using Android ViewModel使用 Android 视图模型
【发布时间】:2017-07-30 17:55:23
【问题描述】:

当我使用ViewModel 测试一个小代码时,我注意到一个逻辑上正确的小问题,我想知道你是如何解决这个问题的。让我们看看这个小班:

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;
    }
}

问题是如果你使用setData()改变LiveDataLiveData观察者注册之前,你的观察者不会在注册观察者后触发。虽然看起来合乎逻辑,但是写异步代码的时候会出问题,在注册observer之前不知道setData()会不会被调用。我想知道当你想注册观察者时如何检查数据是否已经设置。只需检查getData().getValue() != null

另一个问题是数据同步。我是否需要牢记LiveData 同步(就像所有其他正常数据一样),或者LiveData 在内部处理它?比如setData()getData().getValue()可以同时被调用吗?

最后一个问题,似乎LiveData观察者会在你设置一个值时触发,即使它是同一个值(例如,如果你在onLoadFinished()中使用setData()LoadersetData()每次重新创建活动时都会调用)。这将导致使用相同数据调用两次观察者。我想知道防止这种情况的最佳方法是什么。检查ViewModel中的数据是否与我们已有的数据相似,不再设置值?

【问题讨论】:

  • LiveData + ViewModel 存在,所以我认为你不需要使用 Loader。
  • @EpicPandaForce 不,它们的存在是为了解决片段s 和活动之间的数据共享问题。 ViewModel 的片段间数据共享非常棒,但我认为它们不会影响 Loader 的使用。
  • MutableLiveData&lt;List&lt;MyData&gt;&gt; 而不是MutableLiveData&lt;ArrayList&lt;MyData&gt;&gt; 的任何特殊原因?
  • 允许MutableLiveData 中的数据可变并不是一个很好的做法,因此通常将数组列表包装在Collections.unmodifiableList() 中会更好。除非您 set/postValueArrayList 不会阻止它,否则它不应该是可修改的。所以MutableLiveData&lt;List&lt;T&gt;&gt; 使这成为可能:D

标签: android viewmodel


【解决方案1】:

问题是如果你在注册LiveData观察者之前使用setData()改变LiveData,你的观察者注册观察者后不会触发

根据 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 已经有数据集,它将被传递给观察者。。如果没有,那将是一个错误。


另一个问题是数据同步。我是否需要牢记 LiveData 同步(与所有其他普通数据一样),还是 LiveData 在内部处理它?

当 LiveData 中的数据通过 setValue()(仅限主线程)或 postValue() 方法修改时,LiveData 会通知其所有订阅者。

为了使 LiveData 保持最新状态,something 必须使用最新数据更新其值,以便将其“广播”给所有订阅者。

例如,当您从 Room 的 DAO 提供 LiveData&lt;T&gt; getData(); 时,Room 会检查 T 的表是否已修改。如果它被修改,那么它会更新所有使用T 的表的LiveData

您几乎不需要致电getValue()


这将导致使用相同数据调用两次观察者。我想知道防止这种情况的最佳方法是什么。

如果您正确设置了差异或观察逻辑,这应该无关紧要。

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()

【讨论】:

  • So If LiveData already has data set, it will be delivered to the observer.. If it doesn't, then that would be a bug.,我也看到了。但我想这意味着如果你已经注册了观察者。例如,如果您的应用程序重新创建,您之前已经注册了一个观察者,因此您的观察者将被调用一次,因为它的数据之前已更改。但是当应用程序第一次运行时,您不会收到通知,因为观察者是在设置数据后注册的。或者至少,这是我所经历的。
【解决方案2】:

我想有人需要一个LiveData,它只在setData()上调用观察者,我写了这个小班。当您决定在 onLoaderFinished() 上使用 setData() 并重新创建活动时,我自己使用它来防止多个观察者调用。

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);
                }
            }
        });
    }
}

请记住,仅当您想通过设置值来触发观察者并且它不会在活动重新创建时触发时才使用此类。

最好的问候

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多