簡體   English   中英

數據更改時未調用 LiveData onChanged

[英]LiveData onChanged not being called when data changes

我是使用LiveDataViewModels的新手,我不完全了解數據是如何更新的。

根據Android 開發網站

無需在每次應用數據更改時更新 UI,您的觀察者可以在每次發生更改時更新 UI。

這句話對我沒有意義。 我正在使用 Sqlite 數據庫(沒有 Room),並且我在ViewModelgetItems()方法中異步獲取數據。 這是否意味着當在數據庫中添加或更新數據時, 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 )。

您的代碼似乎設置正確。 但是您的ObserversonChanged()方法將被調用一次,因為您的代碼中沒有任何部分通知它您對基礎數據所做的更改。

我建議您使用MutableLiveDatasetValue()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以進行更新。 簡單地刪除這個值而不發布它不會觸發LivedataObserveronchange()方法。 我希望這對某人有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM