Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
419 views
in Technique[技术] by (71.8m points)

android - LiveData is not updating its value after first call

I have been beating my head against the wall and I cannot understand why this is happening. I am working with the new Architectural Components for Android and I am having problems updating a LiveData with a List of Objects. I have two spinners. When i change the option in the first one, The second one must have its content changed. But this last part is not happening. Can anyone help me?

State.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;
}

}

Fragment

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) {

        }
    });

....

Adapter

public void setValues(List<State> states)
{ 
this.states = states; 
Log.d("s", states.isEmpty()+" "+states.toString()); 
notifyDataSetChanged(); 
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Well, I have reached a solution for this issue and found out how this LiveData things works.

Thanks to @MartinMarconcini for all his help is debugging ;)

So apparently, the observers are linked to the object you first set it up to. You cannot replace the object (by attribution) or otherwise it will not work. Also, if the value of your variable is going to change then you should use MutableLiveData

So the change necessary were:

1. Change from LiveData to MutableLiveData and pass that MutableLiveData to the repository when you need to update it

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. In the repository, update the MutableLiveData using 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. Changed the DAO to return List instead of 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.Finally allow to perform queries in the main thread

AppModule.java

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...