I am using the clean architecture with MVVM pattern so the room part goes into the data layer and I'm returning observables from there to domain layer and using them in presentation layer by wrapping them in a LiveData. Now, the problem is that after insertion/deletion/update the list is not getting updated immediately in UI.
Viewmodel in Presentation Layer:
public class WordViewModel extends BaseViewModel<WordNavigator> {
//get all the use cases here
private GetAllWords getAllWords;
private InsertWord insertWord;
private DeleteThisWord deleteThisWord;
private UpdateThisWord updateThisWord;
private GetTheIndexOfTopWord getTheIndexOfTopWord;
//data
public MutableLiveData<List<Word>> allWords;
public WordViewModel(GetAllWords getAllWords, InsertWord insertWord, DeleteThisWord deleteThisWord, UpdateThisWord updateThisWord, GetTheIndexOfTopWord getTheIndexOfTopWord) {
this.getAllWords = getAllWords;
this.insertWord = insertWord;
this.deleteThisWord = deleteThisWord;
this.updateThisWord = updateThisWord;
this.getTheIndexOfTopWord = getTheIndexOfTopWord;
}
public void getAllWords() {
getAllWords.execute(new DisposableObserver<List<Word>>() {
@Override
public void onNext(List<Word> words) {
allWords.setValue(words);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
}, GetAllWords.Params.getAllWords());
}
public void insertWord(Word word) {
insertWord.execute(new DisposableObserver<Boolean>() {
@Override
public void onNext(Boolean aBoolean) {
if (aBoolean)
Log.e("ganesh", "word inserted successfully!!!");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
}, InsertWord.Params.insertWord(word));
}
public void getTheIndexOfTopWord(final String action) {
getTheIndexOfTopWord.execute(new DisposableObserver<Word>() {
@Override
public void onNext(Word word) {
if (word != null)
getNavigator().updateTopIndex(word.getWordId(), action);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
}, GetTheIndexOfTopWord.Params.getTheIndexOfTopWord());
}
public void deleteThisWord(int wordId) {
deleteThisWord.execute(new DisposableObserver<Boolean>() {
@Override
public void onNext(Boolean aBoolean) {
if (aBoolean)
Log.e("ganesh", "word deleted successfully!!!");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
}, DeleteThisWord.Params.deleteThisWord(wordId));
}
public void updateThisWord(int wordId, String newWord) {
updateThisWord.execute(new DisposableObserver<Boolean>() {
@Override
public void onNext(Boolean aBoolean) {
if (aBoolean)
Log.e("ganesh", "word updated successfully!!!");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
}, UpdateThisWord.Params.updateThisWord(wordId, newWord));
}
public MutableLiveData<List<Word>> getWords() {
if (allWords == null) {
allWords = new MutableLiveData<>();
}
return allWords;
}
@Override
protected void onCleared() {
super.onCleared();
if (getAllWords != null)
getAllWords = null;
if (deleteThisWord != null)
deleteThisWord = null;
if (insertWord != null)
insertWord = null;
if (updateThisWord != null)
updateThisWord = null;
if (getTheIndexOfTopWord != null)
getTheIndexOfTopWord = null;
}
}
DAO in Data Layer:
@Dao
public interface WordDao {
@Insert
void insertThisWord(Word word);
@Query("delete from word_table")
void deleteAll();
@Query("select * from word_table order by word_id asc")
List<Word> getAllWords();
@Query("delete from word_table where word_id = :wordId")
void deleteThisWord(int wordId);
@Query("update word_table set word = :newWord where word_id = :wordId")
void updateThisWord(int wordId, String newWord);
@Query("select * from word_table order by word_id asc limit 1")
Word getTheIndexOfTopWord();
}
Repository in Data Layer:
public class WordRepositoryImpl implements WordRepository {
private ApiInterface apiInterface;
private SharedPreferenceHelper sharedPreferenceHelper;
private Context context;
private WordDao wordDao;
private WordRoomDatabase db;
public WordRepositoryImpl(ApiInterface apiInterface, SharedPreferenceHelper sharedPreferenceHelper, WordRoomDatabase db, Context context) {
this.apiInterface = apiInterface;
this.sharedPreferenceHelper = sharedPreferenceHelper;
this.context = context;
this.db = db;
wordDao = db.wordDao();
}
@Override
public Observable<Integer> sum(final int a, final int b) {
return Observable.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return (a + b);
}
});
}
@Override
public Observable<List<Word>> getAllWords() {
return Observable.fromCallable(new Callable<List<Word>>() {
@Override
public List<Word> call() throws Exception {
List<Word> list = new ArrayList<>();
List<com.example.data.models.Word> listWords = db.wordDao().getAllWords();
for (com.example.data.models.Word item : listWords) {
list.add(new Word(item.getWordId(), item.getWord(), item.getWordLength()));
}
return list;
}
});
}
@Override
public Observable<Boolean> insertThisWord(final Word word) {
return Observable.fromCallable(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
com.example.data.models.Word item = new com.example.data.models.Word(word.getWord(), word.getWordLength());
db.wordDao().insertThisWord(item);
return true;
}
});
}
@Override
public Observable<Boolean> deleteThisWord(final int wordId) {
return Observable.fromCallable(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
db.wordDao().deleteThisWord(wordId);
return true;
}
});
}
@Override
public Observable<Boolean> updateThisWord(final int wordId, final String newWord) {
return Observable.fromCallable(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
db.wordDao().updateThisWord(wordId, newWord);
return true;
}
});
}
@Override
public Observable<Word> getTheIndexOfTopWord() {
return Observable.fromCallable(new Callable<Word>() {
@Override
public Word call() throws Exception {
com.example.data.models.Word item = db.wordDao().getTheIndexOfTopWord();
Word word = new Word(item.getWordId(), item.getWord(), item.getWordLength());
return word;
}
});
}
}
GetAllWordsUseCase in Domain Layer:
public class GetAllWords extends UseCase<List<Word>, GetAllWords.Params> {
private WordRepository wordRepository;
public GetAllWords(PostExecutionThread postExecutionThread, WordRepository wordRepository) {
super(postExecutionThread);
this.wordRepository = wordRepository;
}
@Override
public Observable<List<Word>> buildUseCaseObservable(Params params) {
return wordRepository.getAllWords();
}
public static final class Params {
private Params() {
}
public static GetAllWords.Params getAllWords() {
return new GetAllWords.Params();
}
}
}
UseCase Base Class in domain layer:
public abstract class UseCase<T, Params> {
private final PostExecutionThread postExecutionThread;
private final CompositeDisposable compositeDisposable;
public UseCase(PostExecutionThread postExecutionThread) {
this.postExecutionThread = postExecutionThread;
this.compositeDisposable = new CompositeDisposable();
}
/**
* Builds an {@link Observable} which will be used when executing the current {@link UseCase}.
*/
public abstract Observable<T> buildUseCaseObservable(Params params);
/**
* Dispose from current {@link CompositeDisposable}.
*/
public void dispose() {
if (!compositeDisposable.isDisposed()) {
compositeDisposable.dispose();
}
}
/**
* Executes the current use case.
*
* @param observer {@link DisposableObserver} which will be listening to the observable build
* by {@link #buildUseCaseObservable(Params)} ()} method.
* @param params Parameters (Optional) used to build/execute this use case.
*/
public void execute(DisposableObserver<T> observer, Params params) {
if (observer != null) {
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.io())
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
}
/**
* Dispose from current {@link CompositeDisposable}.
*/
private void addDisposable(Disposable disposable) {
if (disposable != null && compositeDisposable != null)
compositeDisposable.add(disposable);
}
}
Finally, WordActivity in Presentation Layer
public class WordActivity extends BaseActivity<WordViewModel> implements
View.OnClickListener, WordNavigator {
@Inject
WordViewModel wordViewModel;
private Button deleteButton, updateButton, addButton;
private EditText editTextWord;
private WordListAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_word);
((MainApplication) getApplicationContext()).getComponent().inject(this);
editTextWord = findViewById(R.id.activity_word_et_word);
deleteButton = findViewById(R.id.activity_main_delete_button);
updateButton = findViewById(R.id.activity_main_update_button);
addButton = findViewById(R.id.activity_word_btn_add_word);
deleteButton.setOnClickListener(this);
updateButton.setOnClickListener(this);
addButton.setOnClickListener(this);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
adapter = new WordListAdapter(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
getViewModel().setNavigator(this);
getViewModel().getAllWords();
getViewModel().getWords().observe(this, new Observer<List<Word>>() {
@Override
public void onChanged(@android.support.annotation.Nullable List<Word> words) {
adapter.setWords(words);
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.activity_main_delete_button:
getViewModel().getTheIndexOfTopWord(TOP_INDEX_ACTION_DELETE);
break;
case R.id.activity_main_update_button:
getViewModel().getTheIndexOfTopWord(TOP_INDEX_ACTION_UPDATE);
break;
case R.id.activity_word_btn_add_word:
handleAddButtonClick();
break;
}
}
public void handleAddButtonClick() {
String text = editTextWord.getText().toString();
if (text.equals("")) {
Toast.makeText(getApplicationContext(), R.string.empty_not_saved, Toast.LENGTH_LONG).show();
} else {
Word word = new Word(text, text.length());
getViewModel().insertWord(word);
editTextWord.setText("");
editTextWord.clearFocus();
}
}
@Override
public void updateTopIndex(Integer wordId, String action) {
if (action.equals(TOP_INDEX_ACTION_DELETE))
getViewModel().deleteThisWord(wordId);
else
getViewModel().updateThisWord(wordId, "dsakagdad");
}
@Override
public WordViewModel getViewModel() {
return wordViewModel;
}
}
**getViewModel().getWords().observe(this, new Observer<List<Word>>() {
@Override
public void onChanged(@android.support.annotation.Nullable List<Word> words) {
adapter.setWords(words);
}
});**
//This portion is getting called only once but not when I
insert/update/delete words from room database!
Can anyone go through these and help me out here!
> from my repository by wrapping the room's List. If you look at my viewmodel, I am then pushing these observables into the LiveData but not getting updates on UI. I will try your solution but not sure if it will work.
– ganeshraj020794 Aug 31 '18 at 05:56> getAllWords(); means observing the changes of the query.