Rxjava2+Retrofit2+MVP的网络请求封装

一、概述

MVP的模式目前在网上已经层出不穷,大多数是通过接口建立关系,进行了功能的实现。看过很多类似的文章,受益良多。

下面切入正题,本文对异常的处理和订阅关系的动态取消进行了封装。通过泛型模式接通了MVP的三层关系。下面简单介绍组成体在本模式中的各个优点:

Retrofit2:通过注解的形式反射出请求体,像调用普通java方法一样的去获取请求对象,底层网络请求基于Okhttp3。

Rxjava2 : 便捷的线程调度,响应式编程,错误信息的简单追踪。

MVP:解耦,复用性好,代码清晰,易维护。

二、代码实现

代码结构目录
  1. 订阅关系的处理:网络请求不止一个,Rxjava2订阅关系就会很多,虽然Rxjava2内部实现了自动取消订阅关系(请求完成,请求报错),但是在特殊情况下需要手动关闭,在结束请求或者在应有的生命周期结束时订阅关系就要取消,比如在请求还没有结束的时候,View在没有执行完毕就退出了,那么它肯定不会自动解绑,这个时候就需要我们手动解除订阅关系,否则会产生内存泄漏。
import io.reactivex.disposables.Disposable;

public interface SubscriptionHelper<T> {
    void add(Disposable subscription);

    void cancel(Disposable t);

    void cancelall();
}

import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;

public class SubscriptionManager implements SubscriptionHelper<Object> {
    public static SubscriptionManager subscriptionManager;
    private CompositeDisposable mDisposables;

    public SubscriptionManager() {
        if (mDisposables == null) {
            mDisposables = new CompositeDisposable();
        }
    }

    @Override
    public void add(Disposable disposable) {
        if (disposable == null) return;
        mDisposables.add(disposable);
    }

    @Override
    public void cancel(Disposable disposable) {
        if (mDisposables != null) {
            mDisposables.delete(disposable);
        }
    }

    @Override
    public void cancelall() {
        if (mDisposables != null) {
            mDisposables.clear();
        }
    }

    public static SubscriptionManager getInstance() {
        if (subscriptionManager == null) {
            synchronized (SubscriptionManager.class) {
                if (subscriptionManager == null) {
                    subscriptionManager = new SubscriptionManager();
                }
            }
        }
        return subscriptionManager;
    }
}

2.网络请求异常的处理:针对常见网络请求异常进行了封装。

public class ErrorBodyDTO {
    private String errCode;
    private String errMsg;

    public String getErrCode() {
        return errCode;
    }

    public void setErrCode(String errCode) {
        this.errCode = errCode;
    }

    public String getErrMsg() {
        return errMsg;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }
}
import android.net.ParseException;

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;

import org.json.JSONException;

import java.io.IOException;
import java.net.ConnectException;

import okhttp3.ResponseBody;

public class ExceptionHandle {
    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;
    private static final int FAIL_QUEST = 406;//无法使用请求的内容特性来响应请求的网页
    private static final int BAD_REQUEST = 400;
    private static ResponseBody body;

    public static ResponeThrowable handleException(Throwable e) {
        ResponeThrowable ex;
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
            switch (httpException.code()) {
                case UNAUTHORIZED:
                   /* body = ((HttpException) e).response().errorBody();
                    try {
                        String message = body.string();
                        Gson gson = new Gson();
                        Exception_401DTO exceptionDTO_401 = gson.fromJson(message, Exception_401DTO.class);
                        //[size=106 text={"error":"invalid_token","error_description":"Invalid access tok…]
                        if (exceptionDTO_401.getError().toString().equals("invalid_token")) {
                            ex.message = "请退出后登录";
                            //发出广播,然后跳转登录
                            ToLoginBean toLoginBean = new ToLoginBean();
                            toLoginBean.setFlag(true);
                            EventBus.getDefault().post(toLoginBean);
                        }
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }*/
                    break;
                case FORBIDDEN:
                    ex.message = "服务器已经理解请求,但是拒绝执行它";
                    break;
                case NOT_FOUND:
                    ex.message = "服务器异常,请稍后再试";
                    break;
                case REQUEST_TIMEOUT:
                    ex.message = "请求超时";
                    break;
                case GATEWAY_TIMEOUT:
                case INTERNAL_SERVER_ERROR:
                    ex.message = "服务器遇到了一个未曾预料的状况,无法完成对请求的处理";
                    break;
                case BAD_REQUEST:
                    /*body = ((HttpException) e).response().errorBody();
                    try {
                        String message = body.string();
                        Gson gson = new Gson();
                        Exception_401DTO exceptionDTO_401 = gson.fromJson(message, Exception_401DTO.class);
                        //[size=106 text={"error":"invalid_token","error_description":"Invalid access tok…]
                        *//**
                 * {"error":"invalid_grant","error_description":"Bad credentials"}
                 *//*
                        if (exceptionDTO_401.getError().toString().equals("invalid_grant")) {
                            ex.message = "用户名或密码错误";
                        }
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }*/
                    break;
                case BAD_GATEWAY:
                case SERVICE_UNAVAILABLE:
                case FAIL_QUEST:
                    body = ((HttpException) e).response().errorBody();
                    try {
                        String message = body.string();
                        Gson gson = new Gson();
                        ErrorBodyDTO globalExceptionDTO = gson.fromJson(message, ErrorBodyDTO.class);
                        if (globalExceptionDTO.getErrMsg() != null) {
                            ex.message = globalExceptionDTO.getErrMsg();
                        } else {
                            ex.message = "";
                        }
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    break;
                default:
                    ex.message = "网络错误";
                    break;
            }
            return ex;
        } else if (e instanceof ServerException) {
            ServerException resultException = (ServerException) e;
            ex = new ResponeThrowable(resultException, resultException.code);
            ex.message = resultException.message;
            return ex;
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
            ex.message = "解析错误";
            return ex;
        } else if (e instanceof ConnectException) {
            ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
            ex.message = "连接失败";
            return ex;
        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {
            ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
            ex.message = "证书验证失败";
            return ex;
        } else if (e instanceof java.net.SocketTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            //ex.message = "连接超时";
            ex.message = "当前网络连接不顺畅,请稍后再试!";
            return ex;
        } else if (e instanceof java.net.UnknownHostException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "网络中断,请检查网络状态!";
            return ex;
        } else if (e instanceof javax.net.ssl.SSLException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "网络中断,请检查网络状态!";
            return ex;
        } else if (e instanceof java.io.EOFException) {
            ex = new ResponeThrowable(e, ERROR.PARSE_EmptyERROR);
            ex.message = "1007";
            return ex;
        } else if (e instanceof java.lang.NullPointerException) {
            ex = new ResponeThrowable(e, ERROR.PARSE_EmptyERROR);
            ex.message = "数据为空,显示失败";
            return ex;
        } else {
            ex = new ResponeThrowable(e, ERROR.UNKNOWN);
            ex.message = "未知错误";
            return ex;
        }
    }


    /**
     * 约定异常
     */
    public class ERROR {
        /**
         * 未知错误
         */
        public static final int UNKNOWN = 1000;
        /**
         * 解析错误
         */
        public static final int PARSE_ERROR = 1001;
        /**
         * 解析no content错误
         */
        public static final int PARSE_EmptyERROR = 1007;
        /**
         * 网络错误
         */
        public static final int NETWORD_ERROR = 1002;
        /**
         * 协议出错
         */
        public static final int HTTP_ERROR = 1003;

        /**
         * 证书出错
         */
        public static final int SSL_ERROR = 1005;

        /**
         * 连接超时
         */
        public static final int TIMEOUT_ERROR = 1006;

        public static final int LOGIN_ERROR = -1000;
        public static final int DATA_EMPTY = -2000;


    }

    public static class ResponeThrowable extends Exception {
        public int code;
        public String message;

        public ResponeThrowable(Throwable throwable, int code) {
            super(throwable);
            this.code = code;
        }

        public ResponeThrowable(String message, int code) {
            this.code = code;
            this.message = message;
        }
    }

    public class ServerException extends RuntimeException {
        public int code;
        public String message;

        public ServerException(int code, String message) {
            this.code = code;
            this.message = message;
        }
    }
}

3.Retrofit2请求体的创建。

import io.reactivex.Observable;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
import utils.BeanGSchaxun;

public interface Apiservice {
    @GET("{fenzhi}{bianhao}/orderinfo")
    Observable<List<BeanGSchaxun>> getGSxin(@Path("fenzhi") String fenzhi,
                                            @Path("bianhao") String bianhao,
                                            @Query("batchNo") String batchNo);

}

4.RetrofitManager管理器的创建,保证Retrofit在类中只有一个实例,避免请求体的多次创建。

import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.neuqsoft.sipay.neuq.Mvp.Service.Apiservice;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitManager {
    private volatile static RetrofitManager retrofitManager;
    private Retrofit retrofit;

    //无参的单利模式
    public static RetrofitManager getSingleton() {
        if (retrofitManager == null) {
            synchronized (RetrofitManager.class) {
                retrofitManager = new RetrofitManager();
            }
        }
        return retrofitManager;
    }


    //无参的构造方法
    private RetrofitManager() {
        initRetrofitManager();
    }

    //构造方法创建Retrofit实例
    private void initRetrofitManager() {
        retrofit = new Retrofit.Builder().baseUrl("url" + "/")
                
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    public Apiservice Apiservice() {
        return retrofit.create(Apiservice.class);
    }
}

5.M层:业务逻辑的处理,实现了观察者中的方法,将其中请求的结果传递到抽象方法中,方便其他类的实现。(注意这里异常的传递和订阅关系的添加)

import java.util.List;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import utils.BeanGSchaxun;
import utils.houtai;

/**
 * Created by Gy 
 */

public class GsModel {

    public void getxinxi(String fenzhi, String bianhao, String shijian, Observer<List<BeanGSchaxun>> observer) {
        Observable<List<BeanGSchaxun>> gSxin = RetrofitManager.getSingleton().Apiservice().getGSxin(fenzhi, bianhao, shijian);
        gSxin.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(observer);

    }

}

import io.reactivex.disposables.Disposable;



public abstract class Observer<T> implements io.reactivex.Observer<T> {
    @Override
    public void onSubscribe(Disposable d) {
        //添加订阅关系
        OnDisposable(d);
    }

    @Override
    public void onNext(T t) {
        OnSuccess(t);
    }

    @Override
    public void onError(Throwable e) {
        //自定义异常的传递
        OnFail(ExceptionHandle.handleException(e));
    }

    @Override
    public void onComplete() {
        OnCompleted();
    }

    public abstract void OnSuccess(T t);

    public abstract void OnFail(ExceptionHandle.ResponeThrowable e);

    public abstract void OnCompleted();

    public abstract void OnDisposable(Disposable d);
}

6.MVP:这里就涉及到泛型的使用了。一般我们建立P和V层是通过实现接口的形式,这里通过泛型的类型规定来绑定我们的P和V。注意的一点是:我们的用户需要什么功能,V层接口就实现几个功能。因为是网络请求的封装,因此要有请求成功和请求失败。看代码(V层接口)。


//SimpleView——Activity里面要实现的方法在这里面定义
public interface SimpleView {
    void onSuccess(Object object);

    void onFail(ExceptionHandle.ResponeThrowable t);

    void OnCompleted();
}

然后V和P的关系建立,先创建基本的P层,它的作用是绑定和解除与V的关系。代码里面有注释。看代码(基本的P层)。

//<里面传入的参数必须是BaseView的子类或者本身>
//这个类的作用就是获取到当前的View
public class BasePresenter<V> {

    public V view;

    //加载View,建立连接
    public void addView(V v) {
        this.view = v;
    }

    //断开连接
    public void detattch() {
        if (view != null) {
            view = null;
        }
    }
}

import java.util.List;

import io.reactivex.disposables.Disposable;
import utils.BeanGSchaxun;
import utils.BeanGSlist;
import utils.BeanGsSecond;
import utils.BeanGsXiadan;


//(Presenter与View交互是通过接口),里面放一个接口
public class Gspresent extends BasePresenter<SimpleView> {
    private GsModel gsModel;

    public Gspresent() {
        gsModel = new GsModel();
    }

    //Presenter与View交互
    public void getGSxinxi(String fenzhi, String bianhao, String shijian) {
        gsModel.getxinxi(fenzhi, bianhao, shijian, new Observer<List<BeanGSchaxun>>() {
            @Override
            public void OnSuccess(List<BeanGSchaxun> beanGSchaxuns) {
                //继承关系,可以使用泛型里面的属性。
                view.onSuccess(beanGSchaxuns);

            }

            @Override
            public void OnFail(ExceptionHandle.ResponeThrowable e) {
                view.onFail(e);
            }

            @Override
            public void OnCompleted() {
                view.OnCompleted();
            }

            @Override
            public void OnDisposable(Disposable d) {
                SubscriptionManager.getInstance().add(d);
            }

        });
    }

}

我们分析下代码,这里涉及到泛型的使用。Gspresent继承BasePresenter<SimpleView>,那么子类就继承了BasePresenter和SimpleView的属性,这样就可以使用BasePresenter里面的变量view去调用SimpleView里面的方法。P和M层的关系建立通过New创建实例即可。
那么Activity的view是怎么传给P的呢?看下面代码。(MVPActivity)

import utils.BaseActivity;
//MvpActivity<P extends BasePresenter>:p继承了BasePresenter,就可以用P的参数去调用BasePresenter
//继承关系
public abstract class MvpActivity<p extends BasePresenter> extends BaseActivity {
    public p presener;

    @Override
    public void initBefore(Bundle savedInstanceState) {
        super.initBefore(savedInstanceState);
        presener = initPresener();
        //把所有继承此类的Activity都绑定到这里了,这样View就和Present联系起来了。
        presener.addView(this);
    }

    protected abstract p initPresener();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presener.detattch();
        //View消除时取消订阅关系
        SubscriptionManager.getInstance().cancelall();
    }
}

在基本的P层中看方法名就知道它是通过addview建立对外关系的,通过addview给view定义属性。这里面的泛型是BasePresenter(基本的P),调用泛型里面的属性方法 presener.addView(this)将Activity的上下文传赋值给view,这样P层就和Activity的View绑定到一起了。
MVPActivity建立了,使用的时候直接继承即可。

import java.util.List;

import butterknife.Bind;
import utils.Title;


public class WXPayEntryActivityc extends MvpActivity<Gspresent> implements SimpleView {

    @Bind(R.id.a)
    Title a;
    @Bind(R.id.xuanzetime)
    Button xuanzetime;
    @Bind(R.id.xinxi)
    Button xinxi;
    @Bind(R.id.time)
    TextView time;
    @Bind(R.id.next)
    Button next;
    @Bind(R.id.Gsxinxi)
    LinearLayout Gsxinxi;
    @Bind(R.id.lvGS)
    ListView lvGS;
    @Bind(R.id.scrollViewa)
    ScrollView scrollViewa;

    @Override
    public int getLayoutid() {
        return R.layout.activity_wxpay_entry_activityc;
    }


    @Override
    public void initView() {
    }

    @Override
    public void initData() {
    }


    @Override
    public void OnCompleted() {
    }

    @Override
    public void onSuccess(Object object) {
        if (object instanceof List) {
           //请求成功
        }

    }


    @Override
    public void onFail(ExceptionHandle.ResponeThrowable t) {
        Log.i(getPackageName(), t.message.toString());
        Toast.makeText(this, t.message.toString(), Toast.LENGTH_SHORT).show();
    }

    @Override
    protected Gspresent initPresener() {
        return new Gspresent();
    }

    }

这里面实现SimpleView 重写Onsuccess方法就可以拿到P层中请求的结果,而不用去做网络请求。

总结

使用泛型可以增加代码的扩展性。
MVP核心就是P层和V层的交互处理,根据V的需求定义P层接口。
欢迎大家的讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,442评论 25 707
  • 许多不管怎么做、怎么想都没结果的事,要懂得交给时间。有些事无论你怎么努力怎么勉强,时间不够,还是耐心的等待吧。 一...
    Kerry202阅读 1,189评论 6 2
  • 我爱你,无论世界多污秽,有多少虚伪不纯粹,都有我在你身边,拼死给你一方碧海蓝天。 这句我爱你也代表父母献给孩子们,...
    气球先生阅读 287评论 2 4
  • 一句问候代表一颗心,话没了……心也没了……
    吴楚红阅读 206评论 0 0
  • 中医虽然没有专门 针对筋缩的疗法,但各种撑拉的方法在习武、气功、瑜加锻炼中一直存在。 目前西医还没有筋缩的概念,很...
    蓝蝶_8263阅读 348评论 0 0