为什么新附加的观察者会触发两次 LiveData 观察者 [英] Why LiveData observer is being triggered twice for a newly attached observer

查看:81
本文介绍了为什么新附加的观察者会触发两次 LiveData 观察者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对LiveData的理解是,它会在数据的当前状态变化上触发观察者,而不是数据的一系列历史状态变化.

My understanding on LiveData is that, it will trigger observer on the current state change of data, and not a series of history state change of data.

目前,我有一个MainFragment,它执行Room写操作,将非垃圾数据更改为垃圾数据.

Currently, I have a MainFragment, which perform Room write operation, to change non-trashed data, to trashed data.

我还有另一个TrashFragment,它观察垃圾数据.

I also another TrashFragment, which observes to trashed data.

考虑以下场景.

  1. 目前有 0 个已删除的数据.
  2. MainFragment 是当前的活动片段.TrashFragment 尚未创建.
  3. MainFragment 添加了 1 个垃圾数据.
  4. 现在,有 1 个垃圾数据
  5. 我们使用导航抽屉,将 MainFragment 替换为 TrashFragment.
  6. TrashFragment 的观察者将首先收到 onChanged,有 0 个 垃圾数据
  7. 同样,TrashFragment 的观察者将第二次收到 onChanged,带有 1 个 垃圾数据
  1. There are currently 0 trashed data.
  2. MainFragment is the current active fragment. TrashFragment is not created yet.
  3. MainFragment added 1 trashed data.
  4. Now, there are 1 trashed data
  5. We use navigation drawer, to replace MainFragment with TrashFragment.
  6. TrashFragment's observer will first receive onChanged, with 0 trashed data
  7. Again, TrashFragment's observer will secondly receive onChanged, with 1 trashed data

出乎我的意料的是,第 (6) 项不应该发生.TrashFragment 应该只接收最新的垃圾数据,即 1.

What is out of my expectation is that, item (6) shouldn't happen. TrashFragment should only receive latest trashed data, which is 1.

这是我的代码

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();
    }
}

<小时>

处理房间的代码

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

知道如何防止针对相同数据接收两次 onChanged 吗?

Any idea how I can prevent from receiving onChanged twice, for a same data?

我创建了一个演示项目来演示这个问题.

I created a demo project to demonstrate this problem.

如你所见,当我在MainFragment中执行写操作(点击ADD TRASHED NOTE按钮)后,当我切换到TrashFragment,我希望 TrashFragment 中的 onChanged 只会被调用一次.但是,它被调用了两次.

As you can see, after I perform write operation (Click on ADD TRASHED NOTE button) in MainFragment, when I switch to TrashFragment, I expect onChanged in TrashFragment will only be called once. However, it is being called twice.

Demo项目可以从https://github.com/yccheok/live-data-下载问题

推荐答案

我分叉了您的项目并对其进行了一些测试.据我所知,你发现了一个严重的错误.

I forked your project and tested it a bit. From all I can tell you discovered a serious bug.

为了使复制和调查更容易,我对您的项目进行了一些编辑.你可以在这里找到更新的项目:https://github.com/techyourchance/live-data-problem .我还向您的 repo 发起了拉取请求.

To make the reproduction and the investigation easier, I edited your project a bit. You can find updated project here: https://github.com/techyourchance/live-data-problem . I also opened a pull request back to your repo.

为了确保不会被忽视,我还在 Google 的打开了一个问题问题跟踪器:

To make sure that this doesn't go unnoticed, I also opened an issue in Google's issue tracker:

重现步骤:

  1. 确保在 MainFragment 中将 REPRODUCE_BUG 设置为 true
  2. 安装应用
  3. 点击添加垃圾笔记"按钮
  4. 切换到 TrashFragment
  5. 请注意,只有一个通知表单 LiveData 具有正确的值
  6. 切换到 MainFragment
  7. 点击添加垃圾笔记"按钮
  8. 切换到 TrashFragment
  9. 请注意,LiveData 有两个通知,第一个通知的值不正确

请注意,如果您将 REPRODUCE_BUG 设置为 false,则该错误不会复制.它表明订阅 LiveData 在MainFragment 改变了 TrashFragment 中的行为.

Note that if you set REPRODUCE_BUG to false then the bug doesn't reproduce. It demonstrates that subscription to LiveData in MainFragment changed the behavior in TrashFragment.

预期结果:在任何情况下都只有一个具有正确值的通知.由于以前的订阅,行为没有变化.

Expected result: Just one notification with correct value in any case. No change in behavior due to previous subscriptions.

更多信息:我查看了一些来源,看起来像由于 LiveData 激活和新的激活而触发的通知观察员订阅.可能与 ComputableLiveData 的方式有关将 onActive() 计算卸载到 Executor.

More info: I looked at the sources a bit, and it looks like notifications being triggered due to both LiveData activation and new Observer subscription. Might be related to the way ComputableLiveData offloads onActive() computation to Executor.

这篇关于为什么新附加的观察者会触发两次 LiveData 观察者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆