单个 ViewModel 中的多个 LiveData 对象
Multiple LiveData objects in single ViewModel
我的应用程序结构如下:
- 主要活动
(Activity) containing Bottom Navigation View with three fragments nested below
- 主页片段
(Fragment) containing TabLayout with ViewPager with following two tabs
- 期刊
(Fragment)
- 书签
(Fragment)
- 片段 B
(Fragment)
- 片段 C
(Fragment)
我正在使用 Room 来维护日记的所有记录。我在 Journal 和 Bookmarks 片段中分别观察一个 LiveData 对象。这些 LiveData 对象由我的 JournalViewModel
class.
返回
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;
}
}
我在两个片段的 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);
}
});
}
但是,当我使用这种方法时出现的问题是,当我删除时,会在任何片段中进行一些更改,例如删除或更新某些日记,其他日记的日期字段会随机更改。
我能够通过使用单个 LiveData 对象并在两个片段中观察它来解决这个问题。我必须在 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 对象并在不同的片段中观察它们。
多个 LiveData 对象不应该在单个 ViewModel 中使用吗?
或
在同时从同一 table 进行更改和获取不同的 LiveData 对象时,是否不允许同一 ViewModel 的两个实例共存?
我找到了导致这个问题的原因。
当我使用 LiveData 并将 getViewLifecycleOwner()
作为 LifecycleOwner
时,我作为参数传递的观察者从未被删除。因此,在切换到不同的选项卡后,有两个活跃的观察者在观察同一 ViewModel 的不同 LiveData
个对象。
解决这个问题的方法是将 LiveData
对象存储在一个变量中,然后在切换到不同片段时删除观察者。
在我的场景中,我通过执行以下操作解决了这个问题:
//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);
}
我的应用程序结构如下:
- 主要活动
(Activity) containing Bottom Navigation View with three fragments nested below
- 主页片段
(Fragment) containing TabLayout with ViewPager with following two tabs
- 期刊
(Fragment)
- 书签
(Fragment)
- 期刊
- 片段 B
(Fragment)
- 片段 C
(Fragment)
- 主页片段
我正在使用 Room 来维护日记的所有记录。我在 Journal 和 Bookmarks 片段中分别观察一个 LiveData 对象。这些 LiveData 对象由我的 JournalViewModel
class.
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;
}
}
我在两个片段的 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);
}
});
}
但是,当我使用这种方法时出现的问题是,当我删除时,会在任何片段中进行一些更改,例如删除或更新某些日记,其他日记的日期字段会随机更改。
我能够通过使用单个 LiveData 对象并在两个片段中观察它来解决这个问题。我必须在 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 对象并在不同的片段中观察它们。
多个 LiveData 对象不应该在单个 ViewModel 中使用吗?
或
在同时从同一 table 进行更改和获取不同的 LiveData 对象时,是否不允许同一 ViewModel 的两个实例共存?
我找到了导致这个问题的原因。
当我使用 LiveData 并将 getViewLifecycleOwner()
作为 LifecycleOwner
时,我作为参数传递的观察者从未被删除。因此,在切换到不同的选项卡后,有两个活跃的观察者在观察同一 ViewModel 的不同 LiveData
个对象。
解决这个问题的方法是将 LiveData
对象存储在一个变量中,然后在切换到不同片段时删除观察者。
在我的场景中,我通过执行以下操作解决了这个问题:
//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);
}