Android LiveData Observer not being triggered second time
Asked Answered
P

5

6

I'm working on an activity where users are allowed to filter their search results. Initially, I fetch all data but when the user wants to filter it, I need to call another query and update the view. I am using Android ROOM and ViewModel for this.

The problem is that each time I fetch new data I need to create a new observer as the old one "stops" firing up. Normally, this should work with one observer, which would be called every time data is updated. Can you help me understand why this is happening, please?

Activity:

package com.jds.fitnessjunkiess.getfitapp.Activities.ExercisesView;

import android.arch.lifecycle.ViewModelProviders;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

import com.jds.fitnessjunkiess.getfitapp.Activities.ExercisesView.Adapters.ExercisesAdapter;
import com.jds.fitnessjunkiess.getfitapp.Data.ViewModels.ExerciseViewModel;
import com.jds.fitnessjunkiess.getfitapp.R;

public class ExercisesViewActivity extends AppCompatActivity implements View.OnClickListener {

  private ExerciseViewModel exerciseViewModel;
  private ExercisesAdapter recyclerViewerAdapter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_exercises_view);

    Toolbar toolbar = findViewById(R.id.toolbar_exercise_view_activity);
    toolbar.setTitle("Exercises");
    setSupportActionBar(toolbar);

    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
      actionBar.setDisplayHomeAsUpEnabled(true);
      actionBar.setDisplayShowHomeEnabled(true);
    }

    RecyclerView recyclerView = findViewById(R.id.exercise_view_recycle_viewer);
    RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
    recyclerView.setLayoutManager(layoutManager);
    this.recyclerViewerAdapter = new ExercisesAdapter();
    recyclerView.setAdapter(recyclerViewerAdapter);

    this.exerciseViewModel = ViewModelProviders.of(this).get(ExerciseViewModel.class);
    this.exerciseViewModel.setFilters("", "");
//    this.exerciseViewModel.selectAll();
    this.exerciseViewModel.select().observe(this, exercises -> {
      if (exercises != null) {
        this.recyclerViewerAdapter.updateDataset(exercises);
      }
    });

    Button button = findViewById(R.id.test_button);
    button.setOnClickListener(this);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case android.R.id.home:
        onBackPressed();
        break;
    }

    return true;
  }

  @Override
  public void onClick(View v) {
    this.exerciseViewModel.setFilters("", "");
//    this.exerciseViewModel.select().observe(this, exercises -> {
//      if (exercises != null) {
//        this.recyclerViewerAdapter.updateDataset(exercises);
//      }
//    });
  }
}

View Model:

public class ExerciseViewModel extends AndroidViewModel {
  ExercisesRepository repository;
  LiveData<List<Exercise>> data;

  public ExerciseViewModel(Application application) {
    super(application);
    this.repository = new ExercisesRepository(application);
  }

  public void setFilters(String muscleGroups, String type) {
    this.data = this.repository.filterSelect(muscleGroups, type);
  }

  public LiveData<List<Exercise>> select() {
    return data;
  }

  public void insert(Exercise exercise) {
    this.repository.insert(exercise);
  }
}

Repository

public class ExercisesRepository {
  private ExerciseDao dao;

  public ExercisesRepository(Application context) {
    WorkoutRoomDatabase database = WorkoutRoomDatabase.getDb(context);
    this.dao = database.exerciseDao();
  }

  public LiveData<List<Exercise>> filterSelect(String muscleGroups, String type) {
    return this.dao.filterSelect("%" + muscleGroups + "%", "%" + type + "%");
  }

  public void insert(Exercise exercise) {
    new insertAsyncTask(this.dao).execute(exercise);
  }

  private static class insertAsyncTask extends AsyncTask<Exercise, Void, Void> {

    private ExerciseDao exerciseDao;

    insertAsyncTask(ExerciseDao  dao) {
      exerciseDao = dao;
    }

    @Override
    protected Void doInBackground(final Exercise... params) {
      exerciseDao.insert(params[0]);
      return null;
    }
  }
}

DAO:

@Dao
public interface ExerciseDao {
  @Query("SELECT * FROM exercises WHERE muscleGroups LIKE :muscleGroup AND type LIKE :type")
  LiveData<List<Exercise>> filterSelect(String muscleGroup, String type);
  @Query("SELECT * FROM exercises")
  LiveData<List<Exercise>> selectAll();
  @Insert
  void insert(Exercise exercise);
  @Update
  void update(Exercise exercise);
  @Delete
  void delete(Exercise exercise);
  @Query("DELETE FROM exercises")
  void deleteAll();
}
Pelerine answered 1/9, 2018 at 23:12 Comment(0)
C
4

You need make some changes because you are reassigning the LiveData object instead of add the items filtered.

You need chante your LiveData> data as MutableLiveData> and in the setFilters method you need get the arraylist from the repository and add it in the MutableLiveData using the setValue method.

ExerciseViewModel

 MutableLiveData<List<Exercise>> data = new MutableLiveData<>();


    public void setFilters(String muscleGroups, String type) {
        List<Exercise> ex = this.repository.filterSelect(muscleGroups, type).getValue();
        this.data.setValue(ex);
    }

I hope it helps you.

Comb answered 2/9, 2018 at 9:4 Comment(6)
Thanks for to code example. I have implemented this change but now results from DB are always NULL but I know there is data in DB matching that query.Pelerine
I forgot create the instance of MutableLiveData: MutableLiveData<List<Exercise>> data = new MutableLiveData<>();Comb
I've created an instance of MutableLiveData in the ViewModel. However, it is the result of DAO that returns LiveData on which I call getValue() and get NULL.Pelerine
So, then you need to check and debug your code. Maybe you have something else. I can't test the whole code. SorryComb
Anyway i am looking your code and you are missing things, Where is your recyclerView declared? Where are you setting the adapter to recyclerview.setAdapter()Comb
I did not include recyclerView as I thought it is irrelevant. I will update my question with full code.Pelerine
L
1

In your setFilters method, you are reassigning a whole new live data to the live data instance you have. Live data only fires event when it's value get changed. So instead you can use a mutable live data, and set it's value by setValue() of this class. And your observer will be called.

Lepanto answered 2/9, 2018 at 0:33 Comment(2)
Thank you! I will try it now and let you know if it worked. :)Pelerine
@Pelerine Did you ever get your code to work? If so, what else did you need to add to get it to work?Dynamotor
S
0

I am facing the same issue my second observer is not called when I go through a button click while when I call in the onCreateView function of BottomSheetDialogFragament both the observers are called.

private fun getKey() {
        if (ApiConstants.publicKey == null) {
            sendOTPViewModel.key.observe(viewLifecycleOwner, Observer { t ->
                if (t == ApiConstants.KEY_SUCCESS) {
                    getKey()
                }
            })

        } else {
            validateUser(number, from, BaseActivity.getDeviceId(mContext))
        }
    }

 private fun validateUser(loginId: String?, loginType: String?, deviceId: String?) {

        sendOTPViewModel.getValidateUser(loginId, loginType, deviceId).observe(viewLifecycleOwner, Observer { t ->
            LoggerUtils.E(TAG, t)
        })

    }

Springtime answered 18/11, 2020 at 11:50 Comment(0)
S
0

Kotlin Answer

Remove these two points in your function if you are using:

  1. = viewModelScope.launch { }
  2. suspend
Sining answered 17/6, 2021 at 17:2 Comment(0)
P
0

Replace viewModelScope to GlobalScope

Perrault answered 12/6, 2023 at 10:58 Comment(1)
Your answer could be improved by providing an example of the solution and how it helps the OP.Flavoprotein

© 2022 - 2024 — McMap. All rights reserved.