![](/img/trans.png)
[英]onChanged is never called when getting data from Firebase using LiveData
[英]LiveData onChanged not being called when data changes
我是使用LiveData
和ViewModels
的新手,我不完全了解數據是如何更新的。
根據Android 開發網站:
無需在每次應用數據更改時更新 UI,您的觀察者可以在每次發生更改時更新 UI。
這句話對我沒有意義。 我正在使用 Sqlite 數據庫(沒有 Room),並且我在ViewModel
的getItems()
方法中異步獲取數據。 這是否意味着當在數據庫中添加或更新數據時, LiveData
會自動檢測它並調用onChange
?
根據 Android 開發網站,它說開始在onCreate
中觀察LiveData
object。 在使用LiveData
之前,我通過查詢數據庫然后調用notifyDataSetChanged()
來刷新onResume
中的數據。
這段代碼我現在在Fragment
加載時顯示數據,但是如果我刪除或向數據庫添加新數據,UI 不會反映更改。 我只是覺得我對LiveData
的工作原理沒有基本的了解,而且我在任何地方都找不到好的解釋。
幫助我 StackOverflow,你是我唯一的希望。
public class MyViewModel extends AndroidViewModel {
private MutableLiveData<List<String>> items;
private Application application;
public MyViewModel(@NonNull Application application) {
super(application);
this.application = application;
}
public MutableLiveData<List<String>> getItems() {
if (items == null) {
items = new MutableLiveData<>();
getItems();
}
return items;
}
private void getItems() {
List<String> items = GetItems(); //async process
this.items.setValue(items)
}
}
public class ListFragment extends Fragment {
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyViewModel 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.getItems().observe(this, observer);
}
}
如果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
需要注入存儲庫 object ,因此需要ViewModelFactory
來構建您的ViewModel
的實例,但總體用法類似於以下內容:
public class MyViewModel extends AndroidViewModel {
...
public MutableLiveData<List<String>> getItems() {
dataRepository.getItems();
}
...
}
要獲取更多信息,請閱讀應用架構指南
您的代碼似乎正確。 您正在正確設置所有內容,每次LiveData
值更改時,您都會在ListFragment
中的Observer
上收到回調。
但是,使用該代碼,LiveData 值將永遠不會改變,因此您的 LiveData 的觀察者只會被調用一次。 您只在onCreate
中調用MyViewModel.getItems
一次,此方法將做兩件事:返回 LiveData 並獲取項目列表。
第一種方法是將 ViewModel 的getItems
方法分為兩種:一種用於獲取 LiveData,另一種用於刷新實際的項目列表。 在 Fragment 中,您將像以前一樣刷新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()
}
}
對於 go 進一步,您需要更改實際刷新項目的方式。 如果你想在數據庫被修改時在你的 ViewModel 中得到通知,你還需要在 ViewModel 和數據庫之間的通信中使用 Observer 模式(Room with LiveData、RxJava 的 Observable 或新的協程 Flow 都是不錯的起點)
這是一個使用 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;
}
}
那里的最后一部分是在您的數據庫 class 中創建該 Observable(使用像 Room 一樣自動執行此操作的庫或通過創建您自己的PublishSubject
)。
您的代碼似乎設置正確。 但是您的Observers
的onChanged()
方法將被調用一次,因為您的代碼中沒有任何部分通知它您對基礎數據所做的更改。
我建議您使用MutableLiveData
的setValue()
或postValue()
方法明確通知您的觀察者數據的變化,具體取決於它們是分別從主線程還是后台線程調用。 或者更好地使用其數據訪問 Object (DAO) 對象來實現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)
}
}
}
從列表中刪除一個值后,新列表將通過postValue()
方法發送到Livedata
以進行更新。 簡單地刪除這個值而不發布它不會觸發Livedata
的Observer
的onchange()
方法。 我希望這對某人有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.