Fragment中调用removeObserver无法移除LiveData观察者问题求助
问题描述
我有一个作为Activity标签页的Fragment,想要在这个Fragment中一次性观察FJViewModel的LiveData,但调用removeObserver后观察者并未被移除,onChanged方法仍反复执行。奇怪的是,用getAllFilters()替代getFiltersById(0)时,removeObserver能正常工作,观察者只执行一次。我只会Java,看不懂Kotlin方案,请问问题出在哪?
相关代码如下:
Fragment代码
public class JInPersonFragment extends Fragment { private FJViewModel fjViewModel; private FragmentJInPersonBinding _InPerson; public static JInPersonFragment newInstance() {return new JInPersonFragment();} @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { _InPerson = FragmentJInPersonBinding.inflate(getLayoutInflater()); fjViewModel = new ViewModelProvider(requireActivity()).get(FJViewModel.class); Observer<Filterj> observer = new Observer<Filterj>() { @Override public void onChanged(Filterj filterj) {// 代码反复进入onChanged方法 Log.i("jinperson",""+(filterj==null)); // ... 其他业务逻辑 fjViewModel.getFiltersById(0).removeObserver(this); } }; fjViewModel.getFiltersById(0).observe(getViewLifecycleOwner(), observer); return _InPerson.getRoot(); } }
ViewModel代码
public class FJViewModel extends AndroidViewModel { private FJRepository repo; private LiveData<List<Filterj>> allFilters; public FJViewModel(@NonNull Application application) { super(application); repo = new FJRepository(application); allFilters = repo.getAllFilters(); } public void insert(Filterj filter) { repo.insert(filter); } public void update(Filterj filter) { repo.update(filter); } public void delete(Filterj filter) { repo.delete(filter); } public LiveData<List<Filterj>> getAllFilters(){ return allFilters; } public LiveData<Filterj> getFiltersById(int id){ return repo.getFiltersById(id); } }
DAO代码
@Dao public interface FJDao { @Insert(onConflict = OnConflictStrategy.REPLACE) void insert(Filterj filter); @Update void update(Filterj filter); @Delete void delete(Filterj filter); @Query("SELECT * FROM filter_jalase ORDER BY id ASC") LiveData<List<Filterj>> getAllFilters(); @Query("SELECT * FROM filter_jalase WHERE id = :id") LiveData<Filterj> getFiltersById(int id); }
问题原因
核心问题在于每次调用getFiltersById(int id)都会返回一个全新的LiveData实例,而getAllFilters()返回的是ViewModel中初始化好的同一个LiveData对象:
- 当你调用
fjViewModel.getFiltersById(0).observe(...)时,观察的是第一个LiveData实例; - 而在
onChanged里调用fjViewModel.getFiltersById(0).removeObserver(this)时,拿到的是第二个全新的LiveData实例,移除操作作用在新实例上,对之前正在被观察的实例完全无效,所以原来的观察者会一直收到回调。
解决方案
要解决这个问题,必须确保观察和移除观察者操作的是同一个LiveData实例,以下是两种可行方案:
方案一:在ViewModel中缓存LiveData实例
修改ViewModel,为需要重复使用的LiveData做缓存(单个id用变量,多个id用Map):
public class FJViewModel extends AndroidViewModel { private FJRepository repo; private LiveData<List<Filterj>> allFilters; // 缓存id=0对应的LiveData private LiveData<Filterj> filterById0; // 如果需要支持多个id,用Map缓存 // private Map<Integer, LiveData<Filterj>> filterMap; public FJViewModel(@NonNull Application application) { super(application); repo = new FJRepository(application); allFilters = repo.getAllFilters(); // filterMap = new HashMap<>(); } // ... 增删改方法保持不变 public LiveData<List<Filterj>> getAllFilters(){ return allFilters; } public LiveData<Filterj> getFiltersById(int id){ if (id == 0) { if (filterById0 == null) { filterById0 = repo.getFiltersById(id); } return filterById0; } // 多id场景的缓存逻辑 // filterMap.computeIfAbsent(id, repo::getFiltersById); // return filterMap.get(id); return repo.getFiltersById(id); } }
方案二:在Fragment中提前保存LiveData实例
在Fragment的onCreateView中先获取一次LiveData实例并保存,后续观察和移除都用这个实例:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { _InPerson = FragmentJInPersonBinding.inflate(getLayoutInflater()); fjViewModel = new ViewModelProvider(requireActivity()).get(FJViewModel.class); // 提前获取并保存目标LiveData实例 LiveData<Filterj> targetLiveData = fjViewModel.getFiltersById(0); Observer<Filterj> observer = new Observer<Filterj>() { @Override public void onChanged(Filterj filterj) { Log.i("jinperson",""+(filterj==null)); // ... 其他业务逻辑 // 用保存的实例移除观察者 targetLiveData.removeObserver(this); } }; targetLiveData.observe(getViewLifecycleOwner(), observer); return _InPerson.getRoot(); }
额外优化:一次性观察工具方法
如果你的需求只是获取一次数据,不需要持续观察,可以自己实现一个一次性观察的工具类,简化代码:
public class LiveDataUtils { public static <T> void observeOnce(LiveData<T> liveData, LifecycleOwner owner, Observer<T> observer) { liveData.observe(owner, new Observer<T>() { @Override public void onChanged(T t) { observer.onChanged(t); liveData.removeObserver(this); } }); } }
在Fragment中使用:
LiveDataUtils.observeOnce(fjViewModel.getFiltersById(0), getViewLifecycleOwner(), filterj -> { Log.i("jinperson",""+(filterj==null)); // ... 其他业务逻辑 });
内容的提问来源于stack exchange,提问作者arman javadi
相关产品推荐
相关产品推荐

