Android MVP Presenter 中引發的空指針異常


一、概述

最近對 googlesamples/android-architecture 中的 MVP-dagger 進行了學習。對照項目的 MVP-dagger 分支,對 MVP-dagger 進行了實踐,不日將會在另一篇文章中進行介紹。

MVP 架構,顧名思義,Model-View-Presenter。其作用是解決 Android 的 MVC 架構中,Activity 的職責不清,過於龐雜,難以維護的缺點。

在眾多對 MVP 的實踐中,Presenter 常有 attachView 和 unattachView 兩個方法,用以建立起 Presenter 同 View 的聯系,便於在 Presenter 中對 View 的接口進行調用。

然而,Presenter 中常常有一些耗時的操作,在某些情況下(諸如用戶退出對應的 View),unAttachView 被調用,此時 Presenter 才完成耗時操作,需要完成對 View 的更新。但此時由於 View 已經被解綁,Presenter 中獲取到的 View 為空,若不進行判空操作,則會引起空指針異常。

在 Presenter 中如何優雅地判空?通過進一步了解 Presenter 的生命周期,能不能找到更好的解決方案?這兩個問題是本文要討論的重點!

二、Presenter 中對 View 判空

2.1 最簡單直接的暴力方式

https://github.com/cnneillee/DailyZHIHU/blob/master/app/src/main/java/com/neil/dailyzhihu/presenter/TopicDetailPresenter.java

public class TopicDetailPresenter extends RxPresenter<TopicDetailContract.View> implements TopicDetailContract.Presenter {
    private RetrofitHelper mRetrofitHelper;

    @Inject
    TopicDetailPresenter(RetrofitHelper retrofitHelper) {
        this.mRetrofitHelper = retrofitHelper;
    }

    @Override
    public void getTopicDetailData(int topicId) {
        mRetrofitHelper.fetchTopicNewsList(topicId).enqueue(new Callback<TopicStoryListBean>() {
            @Override
            public void onResponse(Call<TopicStoryListBean> call, Response<TopicStoryListBean> response) {
                if (response.isSuccessful()) {
                    mView.showContent(response.body());
                }
            }

            @Override
            public void onFailure(Call<TopicStoryListBean> call, Throwable t) {
                mView.showError(t.getMessage());
            }
        });
    }
}

在上面這個例子中,並沒有對 mView 進行判空,當網絡狀態不好,用戶退出當前 Presenter 關聯的 View,就極容易引起空指針異常。

為了避免此問題的出現,應當對 mView 進行判空操作。

if(mView != null) mView.showContent(response.body());
...
if(mView != null) mView.showError(t.getMessage());

這是最直接了當的做法。倘若對整個項目進行如是改造,且不說編碼規范和設計原則的問題,單是修改整個項目的 Presenter 就得費老鼻子勁兒,修改過程也極容易出現遺漏等問題。

暴力××不可取呀!!!

2.2 整合抽象的方式

當然了,上面的代碼在代碼規范和設計模式上也有一定的問題。一種更佳的方式是,不直接讓子 Presenter 對 mView 進行操作,而是使用 getView 方法對 mView 進行暴露,用戶使用 getView 獲取綁定的 view

if(getView() != null) getView().showContent(response.body());
...
if(getView() != null) getView().showError(t.getMessage());

2.3 優雅的判空方式

來自知乎專欄的一片文章 『極光日報 - 不要再在你的 Presenter 中檢查 view != null 啦』 中介紹了一種優雅的方式,拋異常/使用第三方庫。

有另一種我認為更好的方式,就是將 2.2 與 拋異常結合起來。畢竟,誰都不想為了一些小的細節,而引入一個第三方庫。

public class TopicDetailPresenter extends RxPresenter<TopicDetailContract.View> implements TopicDetailContract.Presenter {
    private RetrofitHelper mRetrofitHelper;

    @Inject
    TopicDetailPresenter(RetrofitHelper retrofitHelper) {
        this.mRetrofitHelper = retrofitHelper;
    }

    @Override
    public void getTopicDetailData(int topicId) {
        mRetrofitHelper.fetchTopicNewsList(topicId).enqueue(new Callback<TopicStoryListBean>() {
            @Override
            public void onResponse(Call<TopicStoryListBean> call, Response<TopicStoryListBean> response) {
                if (response.isSuccessful()) {
                    getView().showContent(response.body());
                }
            }

            @Override
            public void onFailure(Call<TopicStoryListBean> call, Throwable t) {
                getView().showError(t.getMessage());
            }
        });
    }
}

在父 Presenter 中

protected View getView(){
	if(mView == null) throw new IllegaStateException("view not attached");
	else return mView;
}

三、Presenter 的生命周期

這個話題源自一篇文章 『Android:聊聊 MVP 中 Presenter 的生命周期』

當然,這篇文章涵蓋了處理 Presenter 的生命周期 與 Activity/Fragment 生命周期同步的問題的幾個框架。同步 Presenter 和 Activity/Fragment 生命周期,從而保證在 View 層(這里姑且 Activity/Fragment 歸類到 View 層吧)生命結束后,Presenter 也被終止生命,故而避免了空指針異常的問題!

這里只引用文中的幾個框架,詳細分析內容,可參見原文!

此文在我的 Github Pages 上同步發布,地址為:Android MVP Presenter 中引發的空指針異常


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM