一、概述
MVP的模式目前在网上已经层出不穷,大多数是通过接口建立关系,进行了功能的实现。看过很多类似的文章,受益良多。
下面切入正题,本文对异常的处理和订阅关系的动态取消进行了封装。通过泛型模式接通了MVP的三层关系。下面简单介绍组成体在本模式中的各个优点:
Retrofit2:通过注解的形式反射出请求体,像调用普通java方法一样的去获取请求对象,底层网络请求基于Okhttp3。
Rxjava2 : 便捷的线程调度,响应式编程,错误信息的简单追踪。
MVP:解耦,复用性好,代码清晰,易维护。
二、代码实现
- 订阅关系的处理:网络请求不止一个,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层接口。
欢迎大家的讨论。