LiveData.observe首参数空值校验及LiveData+MVP架构实践疑问
嘿,咱们来一步步拆解你的问题,结合MVP+LiveData的实践给你梳理清楚~
listLiveData.observe(getView())的参数要不要校验? 答案是必须要!
你想啊,LiveData.observe()方法的第一个参数是LifecycleOwner(你的View层,比如Activity/Fragment),这个参数是不允许为null的——如果getView()返回null(比如View已经被销毁、Presenter还没detach的场景),直接传进去会立刻抛出NullPointerException,这肯定不是你想要的。
所以在调用observe之前,一定要先加判断:
BasketView currentView = getView(); if (currentView == null) { return; } listLiveData.observe(currentView, basketItems -> { callView(view -> view.setData(basketItems)); });
这里和你的callView方法校验逻辑是一致的,都是为了避免在View已经不可用的情况下执行相关操作,既防止崩溃,也避免无效的UI更新。
这个做法是可行的,但有几个关键细节需要注意,不然容易踩内存泄漏或者生命周期的坑:
1. 一定要处理观察者的移除
LiveData会持有LifecycleOwner(也就是你的View)的引用,如果在Presenter detachView的时候不主动移除观察者,当View被销毁后,LiveData还会持有它的引用,就会造成内存泄漏。
所以你需要在detachView方法中添加移除观察者的逻辑:
// 建议在Presenter中保存观察者实例,方便后续移除 private Observer<List<BasketItem>> basketObserver; public void attachView(BasketView view) { this.view = view; listLiveData = MyApplication.me().getDatabase().basketDao().getAll(); // 提前创建观察者实例 basketObserver = basketItems -> { callView(v -> v.setData(basketItems)); }; // 校验后再观察 if (getView() != null) { listLiveData.observe(getView(), basketObserver); } } public void detachView() { if (getView() != null && basketObserver != null) { listLiveData.removeObserver(basketObserver); } this.view = null; }
2. 用弱引用持有View更安全
传统MVP中,如果Presenter直接持有View的强引用,当View销毁但Presenter没被回收时,也会造成内存泄漏。建议把View的引用改成WeakReference:
private WeakReference<BasketView> viewRef; public void attachView(BasketView view) { viewRef = new WeakReference<>(view); // ... 其他逻辑 } private BasketView getView() { return viewRef != null ? viewRef.get() : null; }
3. 关于LiveData的获取时机
Room的Dao方法返回的LiveData是单实例的(多次调用getAll()会拿到同一个LiveData对象),所以不用担心重复观察的问题。但建议在attachView时初始化LiveData和观察者,而不是每次viewIsReady都重新获取,避免不必要的对象创建。
- 调用
LiveData.observe()前必须校验getView()是否为null,防止NPE; - Presenter持有LiveData并以View为观察者的做法是合理的,但一定要在
detachView时移除观察者,同时用弱引用持有View,避免内存泄漏; - 你的
callView校验逻辑已经很完善,保持这个习惯就好。
内容的提问来源于stack exchange,提问作者ip696

