zoukankan      html  css  js  c++  java
  • 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 中引发的空指针异常

  • 相关阅读:
    解决Warning: mysql_connect(): Headers and client library minor version mismatch. 警告
    读取微博feed伪代码
    [待续]不为人知的PHP-SPL标准库
    封装pyMysql
    捉“客”记
    实现小程序插件自定义导航栏
    圆形与矩形的碰撞检测--Mr.Ember
    mpvue原理分析
    webpack学习--Mr.Ember
    原型链、继承--Mr.Ember
  • 原文地址:https://www.cnblogs.com/neillee/p/6800758.html
Copyright © 2011-2022 走看看