LiveData n'est pas la mise à jour de sa valeur après le premier appel
J'ai été me frappant la tête contre le mur et je ne comprends pas pourquoi ce qui se passe. Je suis en train de travailler avec la nouvelle Architecture de Composants pour Android et je vais avoir des problèmes pour mettre à jour un LiveData avec une Liste d'Objets.
J'ai deux roulettes. Quand j'ai modifier l'option dans le premier, Le second doit avoir son contenu a changé. Mais cette dernière partie n'est pas le cas.
Quelqu'un peut-il m'aider?
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) {
}
});
....
Adaptateur
public void setValues(List<State> states)
{
this.states = states;
Log.d("s", states.isEmpty()+" "+states.toString());
notifyDataSetChanged();
}
Il est difficile de voir l'image entière... (j'ai d'abord si vous n'avez pas eu LiveData) mais vous ne. Cependant, je vous conseille de vérifier votre threading / simultanéité, puisque vous êtes à l'aide de l'exécuteur testamentaire de votre repo... L'approche que je voudrais prendre (je pense) c'est que chaque toupie est en observant LiveData (différentes) et le ViewModel ferait en sorte de le mettre à jour lorsque l'INTERFACE utilisateur/Spinner1 changements, de sorte que le LiveData observé par l'INTERFACE utilisateur/Spinner2 changements. Si c'est ce que vous êtes en train de faire de l'atm, il est difficile de voir dans le code.
Je le fais dans le référentiel, car c'est là que j'ai le DAO de l'instance. MON Référentiel est là que je fais les appels à l'API ou de la base de données. Dans ce cas, j'ai seulement besoin d'aller à la bd locale car, auparavant, j'ai récupéré tous les états. De tous mes tests, il peut apparemment obtenir la valeur de la DB, mais le onchange de la méthode de l'observateur n'est pas appelé.
c'est le cas. J'ai deux toupies différentes observation de deux LiveData. J'ai aussi vu quelqu'un parler du problème thread, mais je suis en utilisant le même exemple de la DAO, donc il ne devrait y avoir aucun problème avec ça je pense...
Intéressant de noter que j'utilise tous cette nouvelle la merde avec RXJava2 ainsi... quoique je doute fortement que cela a quelque chose à voir avec quoi que ce soit.
Est votre
Je le fais dans le référentiel, car c'est là que j'ai le DAO de l'instance. MON Référentiel est là que je fais les appels à l'API ou de la base de données. Dans ce cas, j'ai seulement besoin d'aller à la bd locale car, auparavant, j'ai récupéré tous les états. De tous mes tests, il peut apparemment obtenir la valeur de la DB, mais le onchange de la méthode de l'observateur n'est pas appelé.
c'est le cas. J'ai deux toupies différentes observation de deux LiveData. J'ai aussi vu quelqu'un parler du problème thread, mais je suis en utilisant le même exemple de la DAO, donc il ne devrait y avoir aucun problème avec ça je pense...
Intéressant de noter que j'utilise tous cette nouvelle la merde avec RXJava2 ainsi... quoique je doute fortement que cela a quelque chose à voir avec quoi que ce soit.
Est votre
StatesRepository
un singleton? Si vous disposez de deux instances de base de données, des événements, de l'un ne seront pas propagées à l'ensemble des observateurs sur l'autre instance.
OriginalL'auteur joao86 | 2017-08-25
Vous devez vous connecter pour publier un commentaire.
Bien, j'en suis arrivé à une solution à ce problème et trouvé comment ce LiveData choses fonctionne.
Grâce à @MartinMarconcini pour toute son aide est de débogage 😉
Donc, apparemment, les observateurs sont liés à l'objet de la première jusqu'à. Vous ne pouvez pas remplacer l'objet (par attribution) ou sinon il ne fonctionnera pas.
Aussi, si la valeur de votre variable est en passe de changer, alors vous devriez utiliser MutableLiveData
Pour que les modifications nécessaires ont été:
1. Changement de LiveData à MutableLiveData et passer MutableLiveData au référentiel lorsque vous avez besoin de mettre à jour
2. Dans le référentiel, mise à jour de la MutableLiveData à l'aide de setValue
3. Changé le DAO pour retourner la Liste au lieu de LiveData>
4.Enfin permettre d'effectuer des requêtes dans le thread principal
AppModule.java
allowMainThreadQueries()
ne devrait pas vraiment être utilisé pour la Prod... je me suis toujours demandé pourquoi les I ont LiveData être retournés directement à partir de la DAO et vous avez eu à changer que... hmmm. Eh bien, heureux d'entendre cela fonctionne. Bonne chance!Le
allowMainThreadQueries
n'est pas obligatoire, mais pour le moment je vais laisser, juste pour l'auto-garder 🙂Pensez à utiliser RXJava2 pour ces requêtes, il est vraiment utile une fois que vous vous habituez à elle (et vous n'auriez pas besoin d'utiliser un Exécuteur testamentaire, avec tous les problèmes qu'a). 🙂
Je suis en utilisant la liste paginée livedata. son constructeur de la classe renvoie uniquement livedata . Je ne suis pas en mesure de remplacer livedata avec mutable livedata , alors comment puis-je résoudre ce problème . LiveData<PagedList<RecipeListPojo>> liveData= new LivePagedListBuilder(recipeDao.getAllRecipesList(simpleSQLiteQuery), 6).build();
Je n'ai pas essayé d'utiliser PagedList avec LiveData. Il est dans ma liste de choses à faire 🙂
OriginalL'auteur joao86
Dao doit être la même dans toutes les opérations.
Vous utilisez différents Dao exemple, pour insérer et d'observer
OriginalL'auteur Sergey Buzin
Vous ne devez pas mettre à jour le livedata de référence une fois que vous le configurer et commencer à l'observer.Au lieu de mettre à jour les données en direct avec dépôt, vous devriez utiliser le MediatorLiveData.
Dans votre cas, faire les modifications suivantes:
OriginalL'auteur Harish Sharma
Écrit une réponse pour une meilleure discussion.
J'ai donc (dans Kotlin, sry) un modèle qui est une liste de notes (c'est juste un sandbox de l'application de jeu w/tout cela) et voici mon architecture: je n'ai pas de pension sur titres, mais j'ai d'Activité -> ViewModel -> Dao.
Donc Dao présente un
LiveData<MutableList<Note>>
Mon ViewModel... il expose à travers:
val notesList = database.notesDao().loadAll()
et mon Activité (onCreate) ne...
Cela fonctionne. La carte est une carte RecyclerView adaptateur qui n'a littéralement rien, mais:
Si TOUTE autre partie de l'application modifie la liste de notes, la carte met à jour automatiquement. J'espère que cela vous donne une aire de jeux à essayer un simple(r?) approche.
OriginalL'auteur Martin Marconcini