基於Retrofit2+OkHttp3+RxJava實現的MVP框架(2)

接上一篇《基於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的時候更方便。主要還是看大家自己的業務和考慮,哪種比較合適比較方便。


發佈了7 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章