android - 单个 ViewModel 中的多个 LiveData 对象

标签 android android-fragments android-room android-lifecycle android-viewmodel

我的应用程序结构如下:

  • MainActivity( Activity )包含底部导航 View ,下面嵌套了三个 fragment
    • HomeFragment( fragment )包含 TabLayout 和带有以下两个选项卡的 ViewPager
      • 期刊( fragment )
      • 书签( fragment )
    • fragment B( fragment )
    • fragment C( fragment )

我正在使用 Room 来维护日记的所有记录。我在 Journal 和 Bookmarks fragment 中分别观察一个 LiveData 对象。这些 LiveData 对象由我的 JournalViewModel 类返回。

JournalDatabase.java

public abstract class JournalDatabase extends RoomDatabase {

    private static final int NUMBER_OF_THREADS = 4;
    static final ExecutorService dbWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);

    private static JournalDatabase INSTANCE;

    static synchronized JournalDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context.getApplicationContext(), JournalDatabase.class, "main_database")
                    .fallbackToDestructiveMigration()
                    .build();
        }
        return INSTANCE;
    }

    public abstract JournalDao journalDao();
}

JournalRepository.java

public class JournalRepository {
    private JournalDao journalDao;
    private LiveData<List<Journal>> allJournals;
    private LiveData<List<Journal>> bookmarkedJournals;

    public JournalRepository(Application application) {
        JournalDatabase journalDatabase = JournalDatabase.getInstance(application);
        journalDao = journalDatabase.journalDao();
        allJournals = journalDao.getJournalsByDate();
        bookmarkedJournals = journalDao.getBookmarkedJournals();
    }

    public void insert(Journal journal) {
        JournalDatabase.dbWriteExecutor.execute(() -> {
            journalDao.insert(journal);
        });
    }

    public void update(Journal journal) {
        JournalDatabase.dbWriteExecutor.execute(() -> {
            journalDao.update(journal);
        });
    }

    public void delete(Journal journal) {
        JournalDatabase.dbWriteExecutor.execute(() -> {
            journalDao.delete(journal);
        });
    }

    public void deleteAll() {
        JournalDatabase.dbWriteExecutor.execute(() -> {
            journalDao.deleteAll();
        });
    }

    public LiveData<List<Journal>> getAllJournals() {
        return allJournals;
    }

    public LiveData<List<Journal>> getBookmarkedJournals() {
        return bookmarkedJournals;
    }
}

JournalViewModel.java

public class JournalViewModel extends AndroidViewModel {
    private JournalRepository repository;
    private LiveData<List<Journal>> journals;
    private LiveData<List<Journal>> bookmarkedJournals;

    public JournalViewModel(@NonNull Application application) {
        super(application);
        repository = new JournalRepository(application);
        journals = repository.getAllJournals();
        bookmarkedJournals = repository.getBookmarkedJournals();
    }

    public void insert(Journal journal) {
        repository.insert(journal);
    }

    public void update(Journal journal) {
        repository.update(journal);
    }

    public void delete(Journal journal) {
        repository.delete(journal);
    }

    public void deleteAll() {
        repository.deleteAll();
    }

    public LiveData<List<Journal>> getAllJournals() {
        return journals;
    }

    public LiveData<List<Journal>> getBookmarkedJournals() {
        return bookmarkedJournals;
    }
}

我在两个 fragment 的 onActivityCreated() 方法中实例化这个 ViewModel。

JournalFragment.java

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    JournalFactory factory = new JournalFactory(requireActivity().getApplication());
    journalViewModel = new ViewModelProvider(requireActivity(), factory).get(JournalViewModel.class);
    journalViewModel.getAllJournals().observe(getViewLifecycleOwner(), new Observer<List<Journal>>() {
        @Override
        public void onChanged(List<Journal> list) {
            journalAdapter.submitList(list);
        }
    });
}

BookmarksFragment.java

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    JournalFactory factory = new JournalFactory(requireActivity().getApplication());
    journalViewModel = new ViewModelProvider(requireActivity(), factory).get(JournalViewModel.class);
    journalViewModel.getBookmarkedJournals().observe(getViewLifecycleOwner(), new Observer<List<Journal>>() {
        @Override
        public void onChanged(List<Journal> list) {
            adapter.submitList(list);
        }
    });
}

但是,当我使用这种方法时出现的问题是,当我删除时在任何 fragment 中进行一些更改,例如删除或更新某些日志,而其他日志的日期字段会随机更改。

我能够通过使用单个 LiveData 对象并在两个 fragment 中观察它来解决这个问题。我必须在 BookmarkFragment 中进行的更改如下:

BookmarksFragment.java

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    JournalFactory factory = new JournalFactory(requireActivity().getApplication());
    journalViewModel = new ViewModelProvider(requireActivity(), factory).get(JournalViewModel.class);
    journalViewModel.getAllJournals().observe(getViewLifecycleOwner(), new Observer<List<Journal>>() {
        @Override
        public void onChanged(List<Journal> list) {
            List<Journal> bookmarkedJournals = new ArrayList<>();
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).getBookmark() == 1)
                    bookmarkedJournals.add(list.get(i));
            }
            adapter.submitList(bookmarkedJournals);
        }
    });
}

它现在可以正常工作了。

但是,我想知道为什么它无法使用我的第一种方法,即使用两个不同的 LiveData 对象并在不同的 fragment 中观察它们。

多个 LiveData 对象不应该在单个 ViewModel 中使用吗?

在同时从同一个表进行更改和获取不同的 LiveData 对象时,是否不允许同一个 ViewModel 的两个实例共存?

最佳答案

我找到了导致这个问题的原因。

当我使用 LiveData 和 getViewLifecycleOwner() 作为 LifecycleOwner 时,我作为参数传递的观察者从未被删除。因此,在切换到不同的选项卡后,有两个活跃的观察者在观察同一 ViewModel 的不同 LiveData 对象。

解决这个问题的方法是将 LiveData 对象存储在一个变量中,然后在切换到不同 fragment 时移除观察者。

在我的场景中,我通过执行以下操作解决了这个问题:

//store LiveData object in a variable
LiveData<List<Journal>> currentLiveData = journalViewModel.getAllJournals();

//observe this livedata object
currentLiveData.observer(observer);

然后在合适的生命周期方法或任何适合您需要的地方移除这个观察者

@Override
public void onDestroyView() {
    super.onDestroyView();

    //if you want to remove all observers
    currentLiveData.removeObservers(getViewLifecycleOwner());

    //if you want to remove particular observers
    currentLiveData.removeObserver(observer);
}

关于android - 单个 ViewModel 中的多个 LiveData 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61474181/

相关文章:

android - 在选择的底部导航 View 项目上重新创建 fragment

java - NullPointerException...为什么?

java - Android - 动态编辑文本不可见

android-room - 当多个表发生更改时,防止 Room Observable 查询多次触发

android - Kotlin Flow - 可为空值

kotlin - LiveData 总是返回 LiveData<Object>?

android - 为什么我的网络浏览器上的谷歌地图不断加载? (德尔福 XE6 和安卓)

android - OSM - 使用自定义图标显示当前位置

java - Fragment.java 错误 :(94, 51) 错误:不兼容的类型:NewsFragment 无法转换为 Context

java - 按下按钮时退出 fragment 或 Activity