LiveData не обновляет свое значение после первого вызова

государство.java

@Entity(tableName = "states")
public class State{

@PrimaryKey(autoGenerate = false)
private int id;

private String name;

@ColumnInfo(name = "countryId")
private String CountryId;

@Ignore
private Object geoCenter, geoLimit;

public State(){

}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getCountryId() {
    return CountryId;
}

public void setCountryId(String countryId) {
    CountryId = countryId;
}
}

StateDAO

@Dao
public interface StateDao {

@Query("SELECT * FROM states")
LiveData<List<State>> getAllStates();

@Query("SELECT * FROM states WHERE countryId = :countryID")
LiveData<List<State>> getStatesFromCountry(String countryID);

@Query("SELECT COUNT(*) FROM states")
int getNrStates();

@Query("SELECT COUNT(*) FROM states WHERE countryId = :countryID")
int getNrStatesByCountry(String countryID);

@Insert(onConflict = IGNORE)
void insertAll(List<State> states);

@Delete
void delete(State state);
}

StateRepository

@Singleton
public class StatesRepository {

private final WebServices services;
private final StateDao stateDao;
private final Executor executor;

@Inject
public StatesRepository(Executor executor, StateDao stateDao, WebServices services) {
    this.services = services;
    this.stateDao = stateDao;
    this.executor = executor;
}


public LiveData<List<State>> getStates(String token){
    refreshStates(token);

    return stateDao.getAllStates();
}

public LiveData<List<State>> getStatesFromCountry(String countryID){

    return stateDao.getStatesFromCountry(countryID);
}

private void refreshStates(final String token){

    executor.execute(() -> {

        Log.d("oooooo", stateDao.getNrStates() + "");
        if(stateDao.getNrStates() == 0){

            try {
                Response<List<State>> response = services.getStates("Bearer "+token).execute();

                stateDao.insertAll(response.body());

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
}
}

StateViewModel

public class StatesViewModel extends ViewModel {

private LiveData<List<State>> states;
private StatesRepository repo;

@Inject
public StatesViewModel(StatesRepository repository){

    this.repo = repository;
}

public void init(String token){

    states = repo.getStates(token);
}

public void getStatesFromCountry(String countryID){

    states = repo.getStatesFromCountry(countryID);

}

public LiveData<List<State>> getStates(){

    return this.states;
}

}

фрагмент

public class EditAddressFragment extends LifecycleFragment implements View.OnClickListener, Injectable{


private Spinner country, city, state, zip_code;
private String token;
private List<Country> countries;
private List<City> cities;
private List<State> states;
@Inject ViewModelFactory viewModelFactory;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.addresses_edit_layout, container, false);

    city = view.findViewById(R.id.city);
    state = view.findViewById(R.id.state);
    country = view.findViewById(R.id.country);
    ...

    countries = new ArrayList<>();
    cities = new ArrayList<>();
    states = new ArrayList<>();

    return view;
}


@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);


    CountrySpinnerAdapter adapter = new CountrySpinnerAdapter(getActivity(), android.R.layout.simple_spinner_item, countries);
    country.setAdapter(adapter);

    CitySpinnerAdapter cityAdapter = new CitySpinnerAdapter(getActivity(), android.R.layout.simple_spinner_item, cities);
    city.setAdapter(cityAdapter);
    StateSpinnerAdapter stateAdapter = new StateSpinnerAdapter(getActivity(), android.R.layout.simple_spinner_item, states);
    state.setAdapter(stateAdapter);


    CountriesViewModel countriesViewModel = ViewModelProviders.of(this, viewModelFactory).get(CountriesViewModel.class);
    countriesViewModel.init(token);
    countriesViewModel.getCountries().observe(this, adapter::setValues);

    CityViewModel cityViewModel = ViewModelProviders.of(this, viewModelFactory).get(CityViewModel.class);
    cityViewModel.init(token);
    cityViewModel.getCities().observe(this, cityAdapter::setValues);

    StatesViewModel statesViewModel = ViewModelProviders.of(this, viewModelFactory).get(StatesViewModel.class);
    statesViewModel.init(token);
    statesViewModel.getStates().observe(this, states -> { 
      Log.d("called", states.toString()); 
      stateAdapter.setValues(states); } );


    country.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

            Country c = (Country) adapterView.getItemAtPosition(i);

            Log.d("cd", c.getId());

            //states = new ArrayList<State>();

            statesViewModel.getStatesFromCountry(c.getId());

        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    });

....

адаптер

public void setValues(List<State> states)
{ 
this.states = states; 
Log.d("s", states.isEmpty()+" "+states.toString()); 
notifyDataSetChanged(); 
}

4 ответов


Ну, я достиг решения этой проблемы и узнал, как это работает LiveData.

спасибо @MartinMarconcini за всю его помощь в отладке;)

таким образом, по-видимому, наблюдатели связаны с объектом, который вы сначала установили. Вы не можете заменить объект (по атрибуции) или иначе он не будет работать. Кроме того, если значение вашей переменной изменится, вы должны использовать MutableLiveData

Итак, изменение необходимо было:

1. Измените LiveData на MutableLiveData и передайте это MutableLiveData в репозиторий, когда вам нужно его обновить

public class StatesViewModel extends ViewModel {

private MutableLiveData<List<State>> states; ;;CHANGED
private StatesRepository repo;

@Inject
public StatesViewModel(StatesRepository repository){
    this.repo = repository;
}


public void init(String token){

    states = repo.getStates(token);
}

public void getStatesFromCountry(String countryID){

    repo.getStatesFromCountry(this.states, countryID); ;;CHANGED
}

public LiveData<List<State>> getStates(){

    return this.states;
}
}

2. В репозитории обновите MutableLiveData с помощью setValue

@Singleton
public class StatesRepository {

private final WebServices services;
private final StateDao stateDao;
private final Executor executor;

@Inject
public StatesRepository(Executor executor, StateDao stateDao, WebServices services) {
    this.services = services;
    this.stateDao = stateDao;
    this.executor = executor;
}


public MutableLiveData<List<State>> getStates(String token){
    refreshStates(token);

    final MutableLiveData<List<State>> data = new MutableLiveData<>();

    data.setValue(stateDao.getAllStates());

    return data;

}

;; CHANGED
public void getStatesFromCountry(MutableLiveData states, final String countryID){

    states.setValue(stateDao.getStatesFromCountry(countryID));

}

private void refreshStates(final String token){

    executor.execute(() -> {

        if(stateDao.getNrStates() == 0){

            try {
                Response<List<State>> response = services.getStates("Bearer "+token).execute();

                stateDao.insertAll(response.body());

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
}
}

3. Изменен DAO для возврата списка вместо LiveData>

@Dao
public interface StateDao {

@Query("SELECT * FROM states")
List<State> getAllStates();

@Query("SELECT * FROM states WHERE ctrId = :countryID")
List<State> getStatesFromCountry(String countryID);

@Query("SELECT COUNT(*) FROM states")
int getNrStates();

@Query("SELECT COUNT(*) FROM states WHERE ctrId = :countryID")
int getNrStatesByCountry(String countryID);

@Insert(onConflict = IGNORE)
void insertAll(List<State> states);

@Delete
void delete(State state);
}

4.Наконец, разрешить выполнять запросы в основном нить

модуль.java

@Singleton @Provides
AppDatabase provideDb(Application app) {
    return Room.databaseBuilder(app, AppDatabase.class,"unitail.db")
            .allowMainThreadQueries()
            .fallbackToDestructiveMigration()
            .build();
}

написание ответа для лучшего обсуждения.

Итак, у меня есть (в Котлине, sry) модель, которая представляет собой список заметок (Это просто приложение для песочницы, чтобы играть со всем этим), и вот моя архитектура: у меня нет РЕПО, Но у меня есть Activity -> ViewModel -> Dao.

так Дао выставляет LiveData<MutableList<Note>>

@Query("SELECT * FROM notes")
fun loadAll(): LiveData<MutableList<Note>>

моя ViewModel ... выставляет его через:

val notesList = database.notesDao().loadAll()

и моя деятельность (onCreate) делает...

    viewModel.notesList.observe(this,
            Observer<MutableList<Note>> { notes ->
                if (notes != null) {
                    progressBar?.hide()
                    adapter.setNotesList(notes)
                }
            })

это работает. Адаптер адаптер RecyclerView, который буквально ничего не делает, кроме:

 fun setNotesList(newList: MutableList<Note>) {
        if (notes.isEmpty()) {
            notes = newList
            notifyItemRangeInserted(0, newList.size)
        } else {
            val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
                override fun getOldListSize(): Int {
                    return notes.size
                }

                override fun getNewListSize(): Int {
                    return newList.size
                }

                override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                    return notes[oldItemPosition].id == newList[newItemPosition].id
                }

                override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                    val (id, title, _, priority) = newList[newItemPosition]
                    val (id1, title1, _, priority1) = notes[oldItemPosition]
                    return id == id1
                            && priority == priority1
                            && title == title1
                }
            })
            notes = newList
            result.dispatchUpdatesTo(this)
        }
    }

если какая-либо другая часть приложения изменяет этот список заметок, адаптер обновляется автоматически. Я надеюсь, что это дает вам площадку, чтобы попробовать простой(r?) подход.


вы не должны обновлять ссылку livedata после ее установки и начать наблюдать за ней.Вместо этого для обновления живых данных с помощью репозитория вы должны использовать MediatorLiveData.

в вашем случае выполните следующие изменения:

private MediatorLiveData<List<State>> states;  // change
.....
.....
states.addSource(repo.getStatesFromCountry(countryID), newData -> states.setValue(newData)); //change

Dao должен быть одинаковым во всех операциях. Вы используете другой экземпляр Dao для insert и observe