接上一篇《基於Retrofit2+OkHttp3+RxJava實現的MVP框架(1)》- 點擊打開鏈接。
上一篇我們介紹了目前MVP架構中的V和P的實現以及關聯溝通方案,但是我認爲業務核心是數據,它是業務的根基。那麼這篇將着重講解MVP中的Model模塊,以及Model如何與Presenter完成數據交互的。
標題中我們已經透露了,該方案實現中用到的框架是Retrofit2、OkHttp3以及RxJava。我們這裏設計的Model模塊部分,主要是針對HTTP網絡請求類型的數據,如果要考慮本地數據或者其他,結合RxJava一樣可以實現。
首先,上一張類圖。
從圖中看到綠色部分,是MVP的核心類,BasePresenter和BaseView前篇已經介紹過。現在着重講一下BaseManager及其他類。
ServiceFactory
public class ServiceFactory {
private static String TAG = ServiceFactory.class.getSimpleName();
private static class SingletonHolder {
private static final ServiceFactory INSTANCE = new ServiceFactory();
}
/**
* Singleton function
* @return
*/
public static ServiceFactory getInstance() {
return SingletonHolder.INSTANCE;
}
private OkHttpClient mClient;
private Retrofit mRetrofit;
private static Context sContext;
/**
* Should be called at beginning of process
* @param context
*/
public static void initContext(Context context) {
sContext = context;
}
private ServiceFactory() {
initRetrofit();
}
// init configuration of retrofit and okhttp
private void initRetrofit() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// print the log using interceptor
builder.addInterceptor(
new HttpLoggingInterceptor().setLevel(
HttpLoggingInterceptor.Level.BODY));
// http request&response cache dir and file
File file = new File(sContext.getExternalCacheDir(),
Configuration.HTTP_CACHE_DIR);
Cache cache = new Cache(file, Configuration.HTTP_CACHE_SIZE);
mClient = builder
.retryOnConnectionFailure(true)
.connectTimeout(Configuration.HTTP_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(Configuration.HTTP_READ_WRITE_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(Configuration.HTTP_READ_WRITE_TIMEOUT, TimeUnit.SECONDS)
.addInterceptor(new CacheInterceptor()) // ? application interceptor
.addNetworkInterceptor(new CacheInterceptor()) // ? network interceptor
.cache(cache)
.build();
// init retrofit
mRetrofit = new Retrofit.Builder()
.baseUrl(Configuration.HTTP_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // ? async observables
.client(mClient)
.build();
}
/**
* Create service object according to retrofit structure
* @param service
* @param <T>
* @return
*/
public <T> T create(final Class<T> service) {
if (service == null) {
throw new RuntimeException("Api service is null!");
}
return mRetrofit.create(service);
}
ServiceFactory,是作爲協議接口的創建者,其實是對OkHttp和Retrofit的封裝,代碼中的實現其實很多開發者已經完成類似功能,這裏實現的Factory實際是使用了單例模式,也保持了全局唯一的Retrofit對象以及OkHttp對象。方法create(Class<T> services)是基於Retrofit實現創建服務接口的方式向上提供,方便Manager部分創建自己的服務接口對象。BaseManager
public abstract class BaseManager {
/**
* Request executor by default error transformer and result handle policy.
* @param observable
* @param callback
* @param <T>
*/
public final <T extends BaseResult> void execute(Observable<T> observable,
ResponseCallback<T> callback) {
execute1(observable, new ResultHandler<T>(), callback);
}
/**
* Execute the request by custom ResultHandler
* @param observable
* @param resultHandler
* @param callback
* @param <T>
*/
public final <T extends BaseResult> void execute1(Observable<T> observable,
ResultHandler<T> resultHandler,
ResponseCallback<T> callback) {
execute2(observable, new ResultHandler<T>(), new ErrorTransformer<T>(), callback);
}
/**
* Execute request by custom ResultHandler and ErrorTransformer
* @param observable
* @param resultHandler
* @param transformer
* @param callback
* @param <T>
*/
public final <T extends BaseResult> void execute2(Observable<T> observable,
ResultHandler<T> resultHandler,
ErrorTransformer<T> transformer,
ResponseCallback<T> callback) {
if (observable == null) {
return;
}
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.doOnNext(resultHandler)
.onErrorResumeNext(transformer)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(callback);
}
/**
* Execute the request for Type R as final result type.
* @param observable
* @param transformer
* @param callback
* @param <T> Original result type returned by http request according to restful protocol
* @param <R> Final result type returned to up layer in application
*/
public final <T extends BaseResult, R> void executeTransfom(Observable<T> observable,
DataTransformer<T, R> transformer,
ResponseCallback<R> callback) {
executeTransfom1(observable, new ResultHandler<T>(), transformer, callback);
}
/**
* Execute the request for Type R as final result type.
* @param observable
* @param resultHandler
* @param transformer
* @param callback
* @param <T> Original result type returned by http request according to restful protocol
* @param <R> Final result type returned to up layer in application
*/
public final <T extends BaseResult, R> void executeTransfom1(Observable<T> observable,
ResultHandler<T> resultHandler,
DataTransformer<T, R> transformer,
ResponseCallback<R> callback) {
executeTransfom2(observable, resultHandler, transformer, new ErrorTransformer<R>(), callback);
}
/**
* Execute the request for Type R as final result type.
* @param observable
* @param resultHandler
* @param transformer
* @param errorTransformer
* @param callback
* @param <T> Original result type returned by http request according to restful protocol
* @param <R> Final result type returned to up layer in application
*/
public final <T extends BaseResult, R> void executeTransfom2(Observable<T> observable,
ResultHandler<T> resultHandler,
DataTransformer<T, R> transformer,
ErrorTransformer<R> errorTransformer,
ResponseCallback<R> callback) {
if (observable == null) {
return;
}
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.doOnNext(resultHandler)
.flatMap(transformer)
.onErrorResumeNext(errorTransformer)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(callback);
}
/**
* Result handler for result json or exception
*/
public class ResultHandler<T extends BaseResult> implements Action1<T> {
/**
* Implemented by sub class for self handle result.
* Used to do some fake data monitor
* @param data represents the result data
* @return
*/
public boolean handled(T data) {
return false;
}
@Override
public final void call(T data) {
if (data != null) {
// if handle the data by sub class itself
// this will not throw error even result code is not success
if (handled(data)) {
return;
}
// TODO Pre-handle the result code
// TODO If result code is not 200 or custom success code
// TODO Throw a error or exception to go to onError() of ResponseCallback
if (!"0200".equals(data.resultCode)) {
// Wrapper throwable and throw
throw new ServerException(data.resultCode);
}
}
}
}
/**
* Handling the data transforming from result type to custom type.
* Using the flatmap in Rx
* @param <T>
* @param <R>
*/
public abstract class DataTransformer<T extends BaseResult, R> implements Func1<T, Observable<R>> {
@Override
public final Observable<R> call(T data) {
return transformData(data);
}
public abstract Observable<R> transformData(T data);
}
/**
* Transform the common exception to corresponding Observable ResponseThrowable
* @param <T>
*/
public class ErrorTransformer<T> implements Func1<Throwable, Observable<T>> {
@Override
public Observable<T> call(Throwable throwable) {
// Wrapper the exception before result go to onError()
// Or you could handle the data to fake data for success
return Observable.error(ExceptionHandler.handleException(throwable));
}
}
BaseManager是功能接口的封裝和提供者的基類,其子類可以被劃分爲不同的Manager類,比如賬號管理,AccountManager,那麼會提供登錄、註冊等接口,供上層的Presenter使用完成對應功能。但是BaseManager抽象了幾個execute方法。這些方法就是基於RxJava框架的實現,將請求數據的過程與響應結果的過程執行於不同的線程,這樣就很好的解決了線程問題。RxJava的基本知識與使用請自行百度。
在BaseManager裏面,也提供了不同的execute重寫方法,主要是基於一些不同的rx操作符,比如doOnNext、flatMap、onErrorResumeNext。
- doOnNext,是針對在事件流走到最後onNext之前的一個節點,此時可以針對響應數據進行修改。BaseManager中的實現是ResultHandler<T>。
- flatMap,是用來將被觀察者事件流改變成另外的事件流,其實可以理解爲,當前觀察A類型的事件流,到最後要轉化爲B類型的事件流。BaseManager中的實現是DataTransformer<T, R>。
- onErrorResumeNext,是用來出現錯誤,在任何事件流的階段有異常拋出時,能夠將該錯誤引導爲正常的事件流,這裏可以針對不同的錯誤類型,設計正確的事件流結果返回。BaseManager中的實現是ErrorTransformer<T>。
BaseManager中定義的上述三種操作符,可以通過子類Manager來自定義重寫實現,來決定自己的特殊數據處理。基於觀察者模式,使用Rx中的Subscriber來作爲Manager和Presenter的連接橋樑,就可以到M與P的溝通。
ResponseCallback
public abstract class ResponseCallback<T> extends Subscriber<T> {
public abstract void onRequestStart();
public abstract void onSuccess(T result);
public abstract void onRequestFailure(int type, String errorCode);
/**
* Default none handling of custom error handled by sub callback
* They could filter their own error code or type
* @param throwable
* @return
*/
protected boolean filterCustomError(Throwable throwable) {
return false;
}
@Override
public final void onStart() {
super.onStart();
onRequestStart();
}
@Override
public final void onCompleted() {
}
@Override
public final void onError(Throwable throwable) {
// filter the common error should be handled uniformly
if (!filterCustomError(throwable)) {
// handle the common handling
if (throwable instanceof ExceptionHandler.ResponseThrowable) {
ExceptionHandler.ResponseThrowable e =
(ExceptionHandler.ResponseThrowable) throwable;
onRequestFailure(e.type, e.code);
} else {
onRequestFailure(-1, null); // unknown filtered error
}
}
}
@Override
public final void onNext(T result) {
onSuccess(result);
}
}
範型<T>作爲數據結果的類型。根據onNext、onStart、onError來封裝一層方法供調用者自己實現其結果操作。ResponseCallback與BaseManager的實現思路類似,使用者可以通過繼承多態的方式,針對各自的業務流程進行結果的特殊處理,上述代碼就可以描述清楚。
ResponseThrowable是對不同異常類型及信息的特殊封裝。在上文的BaseManager中,ErrorTransformer定義了通用的一場處理流程,並且將不同的Exception進行了統一封裝,使用的是ExceptionHandler。
public class ExceptionHandler {
public static ResponseThrowable handleException(Throwable e) {
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
return new ResponseThrowable(e, String.valueOf(httpException.code()), ERROR.HTTP_ERROR);
} else if (e instanceof ServerException) { // server error
ServerException resultException = (ServerException) e;
return new ResponseThrowable(resultException, resultException.code, ERROR.SERVER_ERROR);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
return new ResponseThrowable(e, ERROR.PARSE_ERROR);
} else if (e instanceof ConnectException) {
return new ResponseThrowable(e, ERROR.NETWORD_ERROR);
} else if (e instanceof javax.net.ssl.SSLHandshakeException) {
return new ResponseThrowable(e, ERROR.SSL_ERROR);
} else if (e instanceof ConnectTimeoutException){
return new ResponseThrowable(e, ERROR.TIMEOUT_ERROR);
} else if (e instanceof java.net.SocketTimeoutException) {
return new ResponseThrowable(e, ERROR.NETWORD_ERROR);
} else {
return new ResponseThrowable(e, ERROR.UNKNOWN);
}
}
/**
* Error type
*/
public static class ERROR {
/**
* Unknown error
*/
public static final int UNKNOWN = 1000;
/**
* Parse error
*/
public static final int PARSE_ERROR = 1001;
/**
* Network error
*/
public static final int NETWORD_ERROR = 1002;
/**
* Http error
*/
public static final int HTTP_ERROR = 1003;
/**
* Certificate error
*/
public static final int SSL_ERROR = 1005;
/**
* Timeout error
*/
public static final int TIMEOUT_ERROR = 1006;
/**
* Server Error
*/
public static final int SERVER_ERROR = 1007;
}
public static class ResponseThrowable extends Exception {
public String code;
public int type;
public ResponseThrowable(Throwable throwable, int type) {
this(throwable, null, type);
}
public ResponseThrowable(Throwable throwable, String code, int type) {
super(throwable);
this.code = code;
this.type = type;
}
}
}
這裏是將不同的錯誤類型做了封裝與轉化,方便上層是用時的統一區分。
我們以Login登錄爲例子,來串接一下整體的設計流程。
LoginManager
public class LoginManager extends BaseManager {
/**
* Login service interface for retrofit with rx
*/
interface LoginService {
@POST("api/login")
Observable<LoginResult> login(@Body LoginParams params); // restful type request using json
}
/**
* Login request
* @param username
* @param password
* @param callback
*/
public void login(String username, String password, LoginResponseCallback callback) {
if (callback != null) {
// 1. Create Login service
LoginService service = ServiceFactory.getInstance().create(LoginService.class);
// 2. Create Login params
LoginParams params = new LoginParams(username, password);
// 3. Create Observable object by calling service function
Observable<LoginResult> observable = service.login(params);
// 4. Execute the request
// execute(observable, callback);
// execute1(observable, new LoginDataHandler(), callback);
execute2(observable, new LoginDataHandler(), new LoginErrorTransformer(), callback);
}
}
/**
* Login response result handler
*/
public abstract static class LoginResponseCallback extends ResponseCallback<LoginResult> {
public abstract void onLoginFailure();
@Override
protected boolean filterCustomError(Throwable throwable) {
// TODO Do some handling to throwable yourself
if (throwable instanceof ExceptionHandler.ResponseThrowable) {
ExceptionHandler.ResponseThrowable serverException
= (ExceptionHandler.ResponseThrowable) throwable;
if (ExceptionHandler.ERROR.SERVER_ERROR == serverException.type) {
// The result will lead to onLoginFailure function for caller
onLoginFailure();
return true;
}
}
// Other exception will lead to the common response handler
return false;
}
}
// login result data handler for fake login success data
private class LoginDataHandler extends ResultHandler<LoginResult> {
@Override
public boolean handled(LoginResult data) {
if (data == null) {
data = new LoginResult();
data.resultCode = "0200";
data.token = "1234567890";
}
return true;
}
}
/**
* Transform the exception to fake login result object.
* Lead the failure result to success one.
*/
private class LoginErrorTransformer extends ErrorTransformer<LoginResult> {
@Override
public Observable<LoginResult> call(Throwable throwable) {
LoginResult data = new LoginResult();
data.resultCode = "0200";
data.token = "1234567890";
return Observable.just(data);
}
}
}
LoginManager定義了登錄服務接口,LoginService,用以描述HTTP接口協議。然後聲明瞭方法login(),作爲上層使用者的調用入口。其內使用的BaseManager的execute2(),方法,重新定義了錯誤處理流程和結果數據處理流程。
重寫了自己的ResponseCallback-LoginResponseCallback,該子類定義了一個錯誤分支,如果是服務器協議類型錯誤,就回調方法onLoginFailure()。
重寫了LoginDataHandler,作爲ResultHandler的實現類,並且重寫了handled方法,將內部結果數據賦值成新的結果對象,這裏就是可以在服務器接口數據異常的時候構造假數據。
重寫了ErrorTransformer-LoginErrorTransformer,作爲異常的轉化者,這裏也是構造了假數據,那麼此時無論什麼錯誤結果,都會正常返回該數據到上層。
LoginPresenter
public void login(String username, String password) {
loginManager.login(username, password, new LoginManager.LoginResponseCallback() {
@Override
public void onLoginFailure() {
if (mView != null) {
mView.onLoginFailure();
}
}
@Override
public void onRequestStart() {
if (mView != null) {
mView.onLogining();
}
}
@Override
public void onSuccess(LoginResult result) {
if (mView != null) {
mView.onLoginSuccess(result.token);
}
}
@Override
public void onRequestFailure(int type, String errorCode) {
if (mView != null) {
mView.onLoginFailure();
}
}
});
}
Presenter中直接創建LoginManager,並且調用其login方法,同時傳入參數以及回調的LoginResponseCallback,此時根據不同的結果狀態回調,來定義LoginView應該進入的展示狀態。
上述內容就是當前設計的基於Retrofit2+OkHttp3+RxJava實現的簡單的MVP框架,基於該框架可以迅速的實現各個業務的功能及其流程,方便定製和擴展。當然了,該設計方案也有待商榷的地方,就是P的存在會無端增加過多的文件和類,如果考慮到這一點,直接將Activity作爲C,直接與Manager交互,那麼就可以切換爲MVC模式,只是在多重業務組合的時候,有P的時候更方便。主要還是看大家自己的業務和考慮,哪種比較合適比較方便。