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

查看:613
本文介绍了为什么为新连接的观察者两次触发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.

演示项目可以从 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 .我还打开了一个拉回请求到您的仓库.

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:

复制步骤:

Steps to reproduce:

  1. 确保在MainFragment中将REPRODUCE_BUG设置为true
  2. 安装应用
  3. 单击添加垃圾便笺"按钮
  4. 切换到TrashFragment
  5. 请注意,只有一个LiveData通知表单具有正确的值
  6. 切换到MainFragment
  7. 单击添加垃圾便笺"按钮
  8. 切换到TrashFragment
  9. 请注意,LiveData有两个通知,第一个通知的值不正确
  1. Ensure that REPRODUCE_BUG is set to true in MainFragment
  2. Install the app
  3. Click on "add trashed note" button
  4. Switch to TrashFragment
  5. Note that there was just one notification form LiveData with correct value
  6. Switch to MainFragment
  7. Click on "add trashed note" button
  8. Switch to TrashFragment
  9. Note that there were two notifications from LiveData, the first one with incorrect value

请注意,如果将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天全站免登陆