数据更改时不调用LiveData onChanged

时间:2019-11-12 21:57:09

标签: android android-livedata android-viewmodel

我不熟悉LiveDataViewModels的使用,而且我不完全了解如何更新数据。

根据Android Dev site

  

您不必每次应用数据更改时都更新UI,   观察者可以在每次更改时更新UI。

那句话对我来说毫无意义。我正在使用Sqlite数据库(没有Room),并且正在getItems()的{​​{1}}方法中异步获取数据。这是否意味着在数据库中添加或更新数据时,ViewModel将自动检测到该数据并调用LiveData

据Android开发者网站称,它开始观察onChange中的LiveData对象。在使用onCreate之前,我通过查询数据库然后调用LiveData来刷新onResume中的数据。

此代码现在在加载notifyDataSetChanged()时显示数据,但是如果我删除数据库或向数据库中添加新数据,则UI不会反映更改。我只是认为我对Fragment的工作方式没有基本的了解,在任何地方都找不到很好的解释。

帮我StackOverflow,您是我唯一的希望。

LiveData

3 个答案:

答案 0 :(得分:4)

如果birthDate尚未初始化,则您只能检索一次:

private MutableLiveData<List<String>> items

解决此问题的一种方法是对数据使用public MutableLiveData<List<String>> getItems() { if (items == null) { items = new MutableLiveData<>(); getItems();// it's called only once } return items; }

ContentObserver

不要忘记注册它来通知数据库中的更改:

...
private final ContentObserver contentObserver = new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        // if there're any changes then perform the request and update the UI
        getItems(); 
    }
};
...

我还建议创建一个单独的... contentResolver.registerContentObserver(<your data content uri>, true, contentObserver); ... 层来负责数据检索,如下所示:

Repository

然后,您的... private final ContentObserver contentObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { // if there're any changes then perform the request and update the UI getItems(); } }; public LiveData<List<String>> getItems() { return new MutableLiveData<String>() { @Override public void onActive() { // your async operation here new Thread({ List<String> items = db.getItems(); postValue(items); }).start(); contentResolver.registerContentObserver(<your data content uri>, true, contentObserver); } @Override public void onInactive() { contentResolver.unregisterContentObserver(contentObserver); } } return liveData; } ... 将需要注入存储库对象,因此将需要ViewModel来构建ViewModelFactory的实例,但总体用法类似于此:

ViewModel

要获取更多信息,请阅读Guide to app architecture

答案 1 :(得分:2)

您的代码似乎正确。您已正确设置了所有内容,并且每次Observer值更改时,都会在ListFragment中的LiveData上收到回调。

但是,使用该代码,LiveData值将永远不会改变,因此您的LiveData的Observer只会被调用一次。您仅在MyViewModel.getItems中调用onCreate一次,此方法将完成两件事:返回LiveData并获取项目列表。

第一种方法是将ViewModel getItems方法分为两种:一种用于获取LiveData,另一种用于刷新实际项目列表。 然后,您可以像以前一样在“片段”中刷新onResume()中的数据。

它看起来像这样:

public class MyViewModel extends ViewModel {
    private final MutableLiveData<List<String>> items = new MutableLiveData<>();

    public LiveData<List<String>> getItemsLiveData() {
        return items;
    }

    public void refreshItems() {
        List<String> items = GetItems(); //async process

        this.items.setValue(items)
    }
}

public class ListFragment extends Fragment {

    private MyViewModel model;

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        model = ViewModelProviders.of(getActivity()).get(MyViewModel.class);

        final Observer<List<String>> observer = new Observer<List<String>>() {
            @Override
            public void onChanged(List<String> items) {
                //update UI
            }
        };

        model.getItemsLiveData().observe(this, observer);
    }

    @Override
    public void onResume() {
        model.refreshItems()
    }
}

要更进一步,您将需要更改实际刷新项目的方式。如果要在修改数据库时在ViewModel中收到通知,则还需要在ViewModel与数据库之间的通信中使用Observer模式(带有LiveData的房间,RxJava的Observable或新的协程Flow是开始的好地方)< / p>

这是一个使用RxJava的伪算法示例(但这取决于您选择要使用的Observer库):

public class MyViewModel extends ViewModel {
    private final MutableLiveData<List<String>> items = new MutableLiveData<>();

    public MyViewModel() {
        // In actual code, don't forget to dispose that subscription in ViewModel.onCleared()
        myDatabase.getDatabaseObservable()
            .subscribe(new Subscriber<List<String>>() {
                @Override
                public void onCompleted() {
                }

                @Override
                public void onNext(List<String> newItems) {
                    items.setValue(newItems)
                }

                @Override
                public void onError() {
                }
            })
    }

    public LiveData<List<String>> getItemsLiveData() {
        return items;
    }
}

其中的最后一部分将是在数据库类中创建该Observable(使用像Room一样自动运行的Library或通过创建自己的PublishSubject)。

答案 2 :(得分:0)

您的代码似乎设置正确。但是您的Observers的{​​{1}}方法将被调用一次,因为您的代码中没有任何部分会通知您您对基础数据所做的更改。

我建议您根据是从主线程还是后台调用onChanged()的{​​{1}}或MutableLiveData方法,将数据的变化显式通知观察者线程。或者最好还是使用其数据访问对象(DAO)对象来实现setValue()数据库。

如果您还不愿意使用postValue(),那么这里是我做了类似操作的代码段:

ROOM

}

从列表中删除值后,新列表将通过ROOM方法发送到class HomeViewModel : ViewModel() { private val _budgetList = MutableLiveData<ArrayList<Budget>>() val budgetList: LiveData<ArrayList<Budget>> = _budgetList private val _billList = MutableLiveData<ArrayList<Bill>>() val billList: LiveData<ArrayList<Bill>> = _billList init { _budgetList.value = getBudgetList() _billList.value = getBills() } fun deleteBudget(pos: Int) { _budgetList.apply { val newValue = value?.apply { removeAt(pos) } header position postValue(newValue) } } 以进行更新。只需删除该值而不发布它,就不会触发Livedata的{​​{1}}的{​​{1}}方法。我希望这可以帮助某人。