接上一篇《基于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的时候更方便。主要还是看大家自己的业务和考虑,哪种比较合适比较方便。