一屎慢、概述
MVP的模式目前在網(wǎng)上已經(jīng)層出不窮瞭稼,大多數(shù)是通過(guò)接口建立關(guān)系,進(jìn)行了功能的實(shí)現(xiàn)腻惠』分猓看過(guò)很多類似的文章,受益良多集灌。
下面切入正題悔雹,本文對(duì)異常的處理和訂閱關(guān)系的動(dòng)態(tài)取消進(jìn)行了封裝。通過(guò)泛型模式接通了MVP的三層關(guān)系欣喧。下面簡(jiǎn)單介紹組成體在本模式中的各個(gè)優(yōu)點(diǎn):
Retrofit2:通過(guò)注解的形式反射出請(qǐng)求體腌零,像調(diào)用普通java方法一樣的去獲取請(qǐng)求對(duì)象,底層網(wǎng)絡(luò)請(qǐng)求基于Okhttp3唆阿。
Rxjava2 : 便捷的線程調(diào)度莱没,響應(yīng)式編程,錯(cuò)誤信息的簡(jiǎn)單追蹤酷鸦。
MVP:解耦饰躲,復(fù)用性好牙咏,代碼清晰,易維護(hù)嘹裂。
二妄壶、代碼實(shí)現(xiàn)
- 訂閱關(guān)系的處理:網(wǎng)絡(luò)請(qǐng)求不止一個(gè),Rxjava2訂閱關(guān)系就會(huì)很多寄狼,雖然Rxjava2內(nèi)部實(shí)現(xiàn)了自動(dòng)取消訂閱關(guān)系(請(qǐng)求完成丁寄,請(qǐng)求報(bào)錯(cuò)),但是在特殊情況下需要手動(dòng)關(guān)閉泊愧,在結(jié)束請(qǐng)求或者在應(yīng)有的生命周期結(jié)束時(shí)訂閱關(guān)系就要取消伊磺,比如在請(qǐng)求還沒(méi)有結(jié)束的時(shí)候,View在沒(méi)有執(zhí)行完畢就退出了删咱,那么它肯定不會(huì)自動(dòng)解綁屑埋,這個(gè)時(shí)候就需要我們手動(dòng)解除訂閱關(guān)系,否則會(huì)產(chǎn)生內(nèi)存泄漏痰滋。
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.網(wǎng)絡(luò)請(qǐng)求異常的處理:針對(duì)常見(jiàn)網(wǎng)絡(luò)請(qǐng)求異常進(jìn)行了封裝摘能。
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;//無(wú)法使用請(qǐng)求的內(nèi)容特性來(lái)響應(yīng)請(qǐng)求的網(wǎng)頁(yè)
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 = "請(qǐng)退出后登錄";
//發(fā)出廣播,然后跳轉(zhuǎn)登錄
ToLoginBean toLoginBean = new ToLoginBean();
toLoginBean.setFlag(true);
EventBus.getDefault().post(toLoginBean);
}
} catch (IOException e1) {
e1.printStackTrace();
}*/
break;
case FORBIDDEN:
ex.message = "服務(wù)器已經(jīng)理解請(qǐng)求敲街,但是拒絕執(zhí)行它";
break;
case NOT_FOUND:
ex.message = "服務(wù)器異常团搞,請(qǐng)稍后再試";
break;
case REQUEST_TIMEOUT:
ex.message = "請(qǐng)求超時(shí)";
break;
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
ex.message = "服務(wù)器遇到了一個(gè)未曾預(yù)料的狀況,無(wú)法完成對(duì)請(qǐng)求的處理";
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 = "用戶名或密碼錯(cuò)誤";
}
} 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 = "網(wǎng)絡(luò)錯(cuò)誤";
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 = "解析錯(cuò)誤";
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 = "證書驗(yàn)證失敗";
return ex;
} else if (e instanceof java.net.SocketTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
//ex.message = "連接超時(shí)";
ex.message = "當(dāng)前網(wǎng)絡(luò)連接不順暢多艇,請(qǐng)稍后再試逻恐!";
return ex;
} else if (e instanceof java.net.UnknownHostException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = "網(wǎng)絡(luò)中斷,請(qǐng)檢查網(wǎng)絡(luò)狀態(tài)峻黍!";
return ex;
} else if (e instanceof javax.net.ssl.SSLException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = "網(wǎng)絡(luò)中斷复隆,請(qǐng)檢查網(wǎng)絡(luò)狀態(tài)!";
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 = "數(shù)據(jù)為空奸披,顯示失敗";
return ex;
} else {
ex = new ResponeThrowable(e, ERROR.UNKNOWN);
ex.message = "未知錯(cuò)誤";
return ex;
}
}
/**
* 約定異常
*/
public class ERROR {
/**
* 未知錯(cuò)誤
*/
public static final int UNKNOWN = 1000;
/**
* 解析錯(cuò)誤
*/
public static final int PARSE_ERROR = 1001;
/**
* 解析no content錯(cuò)誤
*/
public static final int PARSE_EmptyERROR = 1007;
/**
* 網(wǎng)絡(luò)錯(cuò)誤
*/
public static final int NETWORD_ERROR = 1002;
/**
* 協(xié)議出錯(cuò)
*/
public static final int HTTP_ERROR = 1003;
/**
* 證書出錯(cuò)
*/
public static final int SSL_ERROR = 1005;
/**
* 連接超時(shí)
*/
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請(qǐng)求體的創(chuàng)建。
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管理器的創(chuàng)建涮雷,保證Retrofit在類中只有一個(gè)實(shí)例阵面,避免請(qǐng)求體的多次創(chuàng)建。
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;
//無(wú)參的單利模式
public static RetrofitManager getSingleton() {
if (retrofitManager == null) {
synchronized (RetrofitManager.class) {
retrofitManager = new RetrofitManager();
}
}
return retrofitManager;
}
//無(wú)參的構(gòu)造方法
private RetrofitManager() {
initRetrofitManager();
}
//構(gòu)造方法創(chuàng)建Retrofit實(shí)例
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層:業(yè)務(wù)邏輯的處理洪鸭,實(shí)現(xiàn)了觀察者中的方法样刷,將其中請(qǐng)求的結(jié)果傳遞到抽象方法中,方便其他類的實(shí)現(xiàn)览爵。(注意這里異常的傳遞和訂閱關(guān)系的添加)
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) {
//添加訂閱關(guān)系
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層是通過(guò)實(shí)現(xiàn)接口的形式,這里通過(guò)泛型的類型規(guī)定來(lái)綁定我們的P和V蜓竹。注意的一點(diǎn)是:我們的用戶需要什么功能箕母,V層接口就實(shí)現(xiàn)幾個(gè)功能储藐。因?yàn)槭蔷W(wǎng)絡(luò)請(qǐng)求的封裝,因此要有請(qǐng)求成功和請(qǐng)求失敗嘶是「撇看代碼(V層接口)。
//SimpleView——Activity里面要實(shí)現(xiàn)的方法在這里面定義
public interface SimpleView {
void onSuccess(Object object);
void onFail(ExceptionHandle.ResponeThrowable t);
void OnCompleted();
}
然后V和P的關(guān)系建立聂喇,先創(chuàng)建基本的P層辖源,它的作用是綁定和解除與V的關(guān)系。代碼里面有注釋希太】巳模看代碼(基本的P層)。
//<里面?zhèn)魅氲膮?shù)必須是BaseView的子類或者本身>
//這個(gè)類的作用就是獲取到當(dāng)前的View
public class BasePresenter<V> {
public V view;
//加載View,建立連接
public void addView(V v) {
this.view = v;
}
//斷開(kāi)連接
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交互是通過(guò)接口),里面放一個(gè)接口
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) {
//繼承關(guān)系誊辉,可以使用泛型里面的屬性矾湃。
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去調(diào)用SimpleView里面的方法奈偏。P和M層的關(guān)系建立通過(guò)New創(chuàng)建實(shí)例即可坞嘀。
那么Activity的view是怎么傳給P的呢?看下面代碼惊来。(MVPActivity)
import utils.BaseActivity;
//MvpActivity<P extends BasePresenter>:p繼承了BasePresenter丽涩,就可以用P的參數(shù)去調(diào)用BasePresenter
//繼承關(guān)系
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聯(lián)系起來(lái)了裁蚁。
presener.addView(this);
}
protected abstract p initPresener();
@Override
protected void onDestroy() {
super.onDestroy();
presener.detattch();
//View消除時(shí)取消訂閱關(guān)系
SubscriptionManager.getInstance().cancelall();
}
}
在基本的P層中看方法名就知道它是通過(guò)addview建立對(duì)外關(guān)系的矢渊,通過(guò)addview給view定義屬性。這里面的泛型是BasePresenter(基本的P)枉证,調(diào)用泛型里面的屬性方法 presener.addView(this)將Activity的上下文傳賦值給view矮男,這樣P層就和Activity的View綁定到一起了。
MVPActivity建立了室谚,使用的時(shí)候直接繼承即可毡鉴。
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) {
//請(qǐng)求成功
}
}
@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();
}
}
這里面實(shí)現(xiàn)SimpleView 重寫Onsuccess方法就可以拿到P層中請(qǐng)求的結(jié)果,而不用去做網(wǎng)絡(luò)請(qǐng)求秒赤。
總結(jié)
使用泛型可以增加代碼的擴(kuò)展性猪瞬。
MVP核心就是P層和V層的交互處理,根據(jù)V的需求定義P層接口入篮。
歡迎大家的討論陈瘦。