web-dev-qa-db-fra.com

Pourquoi l'observateur LiveData est déclenché deux fois pour un nouvel observateur attaché

Ma compréhension de LiveData est que cela déclenchera l'observateur sur le changement d'état actuel des données, et non sur une série de changements d'état des données d'historique.

Actuellement, j'ai un MainFragment, qui effectue une opération d'écriture Room, pour changer les données non supprimées , en données supprimées .

J'ai aussi un autre TrashFragment, qui observe les données trashed .

Considérez le scénario suivant.

  1. Il y a actuellement 0 données supprimées .
  2. MainFragment est le fragment actif actuel. TrashFragment n'est pas encore créé.
  3. MainFragment a ajouté 1 données supprimées .
  4. Maintenant, il y a 1 données trashées
  5. Nous utilisons le tiroir de navigation, pour remplacer MainFragment par TrashFragment.
  6. L'observateur de TrashFragment recevra d'abord onChanged, avec 0 données trashées
  7. Encore une fois, l'observateur de TrashFragment recevra en second lieu onChanged, avec 1 données trashées

Ce qui est hors de mon attente, c'est que le point (6) ne devrait pas se produire. TrashFragment ne devrait recevoir que les dernières données trashed , qui est 1.

Voici mes codes


TrashFragment.Java

public class TrashFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        noteViewModel = ViewModelProviders.of(getActivity()).get(NoteViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...

        noteViewModel.getTrashedNotesLiveData().removeObservers(this);
        noteViewModel.getTrashedNotesLiveData().observe(this, notesObserver);

MainFragment.Java

public class MainFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        noteViewModel = ViewModelProviders.of(getActivity()).get(NoteViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...

        noteViewModel.getNotesLiveData().removeObservers(this);
        noteViewModel.getNotesLiveData().observe(this, notesObserver);

NoteViewModel .Java

public class NoteViewModel extends ViewModel {
    private final LiveData<List<Note>> notesLiveData;
    private final LiveData<List<Note>> trashedNotesLiveData;

    public LiveData<List<Note>> getNotesLiveData() {
        return notesLiveData;
    }

    public LiveData<List<Note>> getTrashedNotesLiveData() {
        return trashedNotesLiveData;
    }

    public NoteViewModel() {
        notesLiveData = NoteplusRoomDatabase.instance().noteDao().getNotes();
        trashedNotesLiveData = NoteplusRoomDatabase.instance().noteDao().getTrashedNotes();
    }
}

Code qui traite de la chambre

public enum NoteRepository {
    INSTANCE;

    public LiveData<List<Note>> getTrashedNotes() {
        NoteDao noteDao = NoteplusRoomDatabase.instance().noteDao();
        return noteDao.getTrashedNotes();
    }

    public LiveData<List<Note>> getNotes() {
        NoteDao noteDao = NoteplusRoomDatabase.instance().noteDao();
        return noteDao.getNotes();
    }
}

@Dao
public abstract class NoteDao {
    @Transaction
    @Query("SELECT * FROM note where trashed = 0")
    public abstract LiveData<List<Note>> getNotes();

    @Transaction
    @Query("SELECT * FROM note where trashed = 1")
    public abstract LiveData<List<Note>> getTrashedNotes();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract long insert(Note note);
}

@Database(
        entities = {Note.class},
        version = 1
)
public abstract class NoteplusRoomDatabase extends RoomDatabase {
    private volatile static NoteplusRoomDatabase INSTANCE;

    private static final String NAME = "noteplus";

    public abstract NoteDao noteDao();

    public static NoteplusRoomDatabase instance() {
        if (INSTANCE == null) {
            synchronized (NoteplusRoomDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(
                            NoteplusApplication.instance(),
                            NoteplusRoomDatabase.class,
                            NAME
                    ).build();
                }
            }
        }

        return INSTANCE;
    }
}

Une idée comment je peux empêcher de recevoir onChanged deux fois, pour une même donnée?


Démo

J'ai créé un projet de démonstration pour illustrer ce problème.

Comme vous pouvez le voir, après avoir effectué une opération d'écriture (cliquez sur le bouton ADD TRASHED NOTE ) dans MainFragment, lorsque je passe à TrashFragment, je attendez que onChanged dans TrashFragment ne sera appelé qu'une seule fois. Cependant, il est appelé deux fois.

enter image description here

Le projet de démonstration peut être téléchargé à partir de https://github.com/yccheok/live-data-problem

49
Cheok Yan Cheng

Si vous cherchez une solution pour éviter les déclencheurs multiples sur popUp la pile arrière du fragment de destination au fragment d'origine

Ma solution consiste à observer les données en direct sur onCreate () du cycle de vie du fragment avec le propriétaire du cycle de vie comme activité et de supprimer l'observateur sur onDestroy () du cycle de vie du fragment

0
Muhamed Riyas M