How To Retrieve List Of Objects From Dao Via Livedata<> In View Model When There Is No Change To Trigger Onchange Method In View Model In Android
Solution 1:
LiveData solution
@Zohaib Amir's comment is correct. You can call LiveData#observe()
on anywhere you want as long as you remember to clear the observers. One drawback with this approach is that you have to keep the reference to these observers and LiveData
already has Transformations.switchMap()
so that you can avoid that.
ViewModel
// Instance variable that stores the current dictionaryId that user wants to see.privatefinalMutableLiveData<Long> currentDictionaryId = new MutableLiveData<>();
// Instance variable that stores the current list of words. This will automatically change when currentDictionaryId value changes.privatefinalLiveData<List<Word>> words =Transformations.switchMap(currentDictionaryId, dictionaryId ->
mWordRepository.getWordByDictionaryId(dictionaryId));
// Set new dictionaryIdpublic void setDictionaryId(long dictionaryId) {
currentDictionaryId.postValue(dictionaryId);
}
// Get LiveData to listen topublicLiveData<List<Word>> getWords() {
return words;
}
Activity
// onCreate()// get all words from WordDao
mWordViewModel = ViewModelProviders.of(MainActivity.this).get(WordViewModel.class);
mWordViewModel.setDictionaryId(dictionaryId);
mWordViewModel.getWords().observe(MainActivity.this, new Observer<List<Word>>() {
@Overridepublic void onChanged(@Nullable List<Word> words) {
mainAdapter.setWords(words);
mAllWords = words;
mainAdapter.notifyDataSetChanged();
}
});
// note that the order of calling setDictionaryId and getWords doesn't matter. setDictionaryId is supposed to call at anytime.
// OnOptionsItemSelected
...
changeDictionaryDialog.setView(view);
changeDictionaryDialog.setPositiveButton("OK", newDialogInterface.OnClickListener() {
@OverridepublicvoidonClick(DialogInterface dialog, int which) {
finalStringdictionaryValue= dictionarySpinner.getSelectedItem().toString();
for (Dictionary dictionary : mDictionaryList) {
if (dictionary.getDictionaryName().trim().equals(dictionaryValue)) {
dictionaryId = dictionary.getDid();
mWordViewModel.setDictionaryId(dictionaryId);
break;
}
}
}
});
...
This time I prepared other solutions so that you can compare. Other solutions (almost) do the exact same thing.
Non-reactive solution
You can implement above feature based on callbacks, and this means that you have to manually handle lifecycle changes, notifications, and threading. Also, there is a functional difference that LiveData
will automatically notify the observer when there is a change in the db, while this callback design is just one-time look up only.
In this example, we will use Executor
to execute tasks in the background thread.
WordDao
@Query("SELECT * FROM word_table WHERE dictionary_id =:dictionaryId")public List<Word> getWordsByDictionaryID(long dictionaryId); // notice that livedata is gone.
WordRepository Database access must be done in a background thread. So when we access db, we need to switch to a background thread at some point. In this example, we will switch to the background thread in this layer.
// This executor is needed to run Runnables on a background thread. In real application, you may// create this executor outside of this repository and later inject it to this repository.privatefinalExecutorioExecutor= Executors.newSingleThreadExecutor();
privatevoidgetWordByDictionaryId(long dictionaryId, Consumer<List<Word>> callback) {
ioExecutor.execute(() -> {
List<Word> wordList = wordDao.getWordsByDictionaryId(dictionaryId);
callback.accept(wordList);
});
}
WordViewModel There isn't much ViewModel does in this example. Just pass the parameters to the repository.
publicvoidgetWordByDictionaryId(long dictionaryId, Consumer<List<Word>> callback){
mWordRepository.getWordByDictionaryId(dictionaryId, callback);
}
Activity
Note that Consumer#accept
will run on a background thread. Therefore before you do anything with the ui, you need to switch back to the ui thread.
// onCreate
mWordViewModel = ViewModelProviders.of(MainActivity.this).get(WordViewModel.class);
mWordViewModel.getWordByDictionaryId(dictionaryId, words -> {
runOnUiThread(() -> {
if (isFinishing() || isDestroyed()) return; // if the activity is finishing, don't do anything.
mainAdapter.setWords(words);
mAllWords = words;
mainAdapter.notifyDataSetChanged();
});
});
// onOptionsItemSelected
changeDictionaryDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Overridepublic void onClick(DialogInterface dialog, int which) {
final String dictionaryValue = dictionarySpinner.getSelectedItem().toString();
for (Dictionary dictionary : mDictionaryList) {
if (dictionary.getDictionaryName().trim().equals(dictionaryValue)) {
dictionaryId = dictionary.getDid();
mWordViewModel.getWordByDictionaryId(dictionaryId, words -> {
runOnUiThread(() -> {
if (isFinishing() || isDestroyed()) return; // if the activity is finishing, don't do anything.
mainAdapter.setWords(words);
mAllWords = words;
mainAdapter.notifyDataSetChanged();
});
});
break;
}
}
}
});
RxJava
RxJava
solution will look much like the LiveData
solution. It has many advantages over LiveData
: it has lots of observables and operators to work with. These advantages are more apparent in more complex applications, where you need to do conditional or delayed async tasks, periodic tasks, multiple requests chained one after the other, error handlings, etc.
WordDao
@Query("SELECT * FROM word_table WHERE dictionary_id =:dictionaryId")public Flowable<List<Word>> getWordsByDictionaryID(long dictionaryId);
ViewModel
privatefinal BehaviorProcessor<Long> currentDictionaryId = BehaviorProcessor.create();
// Set new dictionaryIdpublicvoidsetDictionaryId(long dictionaryId) {
currentDictionaryId.onNext(dictionaryId);
}
// Get Flowable to listen topublic Flowable<List<Word>> getWords() {
return currentDictionaryId.switchMap(dictionaryId -> mWordRepository
.getWordByDictionaryId(dictionaryId)
// add ".take(1)" if you need one-time look up.
.subscribeOn(Schedulers.io()));
}
Activity
private fianl CompositeDisposable disposables = new CompositeDisposable();
// onCreate()
mWordViewModel = ViewModelProviders.of(MainActivity.this).get(WordViewModel.class);
mWordViewModel.setDictionaryId(dictionaryId);
disposables.add(mWordViewModel.getWords()
.observeOn(AndroidSchedulers.mainThread()))
.subscribe(words -> {
mainAdapter.setWords(words);
mAllWords = words;
mainAdapter.notifyDataSetChanged();
});
// onOptionsItemSelected()
changeDictionaryDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
publicvoidonClick(DialogInterface dialog, int which) {
final String dictionaryValue = dictionarySpinner.getSelectedItem().toString();
for (Dictionary dictionary : mDictionaryList) {
if (dictionary.getDictionaryName().trim().equals(dictionaryValue)) {
dictionaryId = dictionary.getDid();
mWordViewModel.setDictionaryId(dictionaryId);
break;
}
}
}
});
// onDestroy()
disposables.clear();
Post a Comment for "How To Retrieve List Of Objects From Dao Via Livedata<> In View Model When There Is No Change To Trigger Onchange Method In View Model In Android"