The solution proposed by @CommonsWare and @Toni Joe didn't solve the issue for me when I needed to remove the observers after receiving the first result from a DAO query in my ViewModel. However, the following solution found at Livedata keeps observer after calling removeObserer did the trick for me with a little of my own intuition.
The process is as follows, create a variable in your ViewModel where the LiveData is stored upon request, retrieve it in a create observer function call in the activity after doing a null check, and call a remove observers function before calling the flushToDB routine in an imported class. That is, the code in my ViewModel looks as follows:
public class GameDataModel extends AndroidViewModel {
private LiveData<Integer> lastMatchNum = null;
.
.
.
private void initLastMatchNum(Integer player1ID, Integer player2ID) {
List<Integer> playerIDs = new ArrayList<>();
playerIDs.add(player1ID);
playerIDs.add(player2ID);
lastMatchNum = mRepository.getLastMatchNum(playerIDs);
}
public LiveData<Integer> getLastMatchNum(Integer player1ID, Integer player2ID) {
if (lastMatchNum == null) { initLastMatchNum(player1ID, player2ID); }
return lastMatchNum;
}
In the above, if there is no data in the LiveData variable in the ViewModel, I call initLastMatchNum()
to retrieve the data from a function within the view model. The function to be called from the activity is getLastMatchNum()
. This routine retrieves the data in the variable in the ViewModel (which is retrieved via the repository via the DAO).
The following code I have in my Activity
public class SomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
.
.
.
setupLastMatchNumObserver();
.
.
.
}
private void setupLastMatchNumObserver() {
if (mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID).hasObservers()) {
Log.v("Observers", "setupLastMatchNumObserver has observers...returning");
return;
}
Log.v("Setting up Observers", "running mGameDataViewModel.get...observer()");
mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID).observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer MatchNumber) {
if (MatchNumber == null ) {
matchNumber = 1;
Log.v( "null MatchNumber", "matchNumber: " + matchNumber.toString());
}
else {
matchNumber = MatchNumber; matchNumber++;
Log.v( "matchNumber", "Incrementing match number: " + matchNumber.toString());
}
MatchNumberText.setText(matchNumber.toString());
}
});
}
private void removeObservers() {
final LiveData<Integer> observable = mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID);
if (observable != null && observable.hasObservers()) {
Log.v("removeObserver", "Removing Observers");
observable.removeObservers(this);
}
}
What's going on in the above, is 1.) I call the setupLastMatchNumObserver()
routine in the onCreate
method of the activity, to update the class's variable matchNum
. This keeps track of the match numbers between players in my game which is stored in a database. Every set of players will have a different match number in the database based upon how often they play new matches with each other. The first solutions in this thread seemed a little weary to me as calling remove observers in the onChanged
seems strange to me and would constantly change the TextView
object after every database flush of each move of the players. So matchNumber
was getting incremented after every move because there was a new value in the database after the first move (namely the one matchNumber++
value) and onChanged
kept being called because removeObservers
was not working as intended. setupLastMatchNumObserver()
checks to see if there are observers of the live data and if so does not instantiate a new call each round. As you can see I am setting a TextView
object to reflect the current matchnumber of the players.
The next part is a little trick on when to call removeObservers()
. At first I thought if I called it directly after setupLastMatchNumObserver()
in the onCreate
override of the activity that all would be fine. But it removed the observer before the observer could grab the data. I found out that if I called removeObservers()
directly prior to the call to flush the new data collected in the activity to the database (in separate routines throughout the activity) it worked like a charm. i.e.,
public void addListenerOnButton() {
.
.
.
@Override
public void onClick(View v) {
.
.
.
removeObservers();
updateMatchData(data);
}
}
I also call removeObservers();
and updateMatchData(data)
in other places in my activity in the above fashion. The beauty is removeObservers()
can be called as many times as needed since there is a check to return if there are no observers present.