在 App 開發(fā)中網(wǎng)絡(luò)請(qǐng)求是每個(gè)開發(fā)者必備的開發(fā)庫,也出現(xiàn)了許多優(yōu)秀開源的網(wǎng)絡(luò)請(qǐng)求庫。例如

這些網(wǎng)絡(luò)請(qǐng)求庫很大程度上提高程序猿的編碼效率。但是隨著業(yè)務(wù)的發(fā)展,App 變得越來越大,我們將這些網(wǎng)絡(luò)請(qǐng)求庫加入到項(xiàng)目中直接使用,對(duì)我們業(yè)務(wù)類的入侵是非常強(qiáng)的。如果要進(jìn)行業(yè)務(wù)分離時(shí),這些網(wǎng)絡(luò)請(qǐng)求代碼將是一個(gè)阻止我們進(jìn)一步工作的絆腳石。對(duì)開發(fā)者來說是非常痛苦的。

因此我們構(gòu)建的網(wǎng)絡(luò)請(qǐng)求框架要可以解決以下問題:

  • 分離業(yè)務(wù)與網(wǎng)絡(luò)請(qǐng)求代碼

  • 網(wǎng)絡(luò)庫可以很容易的被替換

  • 網(wǎng)絡(luò)庫可以很方便的復(fù)用

所以在 App 組件化/模塊化開發(fā)架構(gòu)思路 一文中,我們把網(wǎng)絡(luò)請(qǐng)求作為內(nèi)核層的一個(gè)組件。

封裝第三方網(wǎng)絡(luò)請(qǐng)求接口

一般來說,目前絕大部分 App 的數(shù)據(jù)請(qǐng)求都是使用 HTTP 協(xié)議,而數(shù)據(jù)交換的協(xié)議使用 json 格式。因此可以封裝一個(gè)通用的請(qǐng)求接口。(當(dāng)然還有其他一些協(xié)議,例如微信的 mars ,但是封裝的思路是一致的,本文為了簡(jiǎn)單說明,暫時(shí)使用通用網(wǎng)絡(luò)請(qǐng)求框架,不排除以后會(huì)對(duì) mars 的封裝)

首先預(yù)覽一下框架結(jié)構(gòu)

IRequest

這個(gè)類封裝了網(wǎng)絡(luò)請(qǐng)求的通用接口,定義請(qǐng)求接口 doRequest() 、獲取請(qǐng)求連接 getUrl() 、獲取請(qǐng)求方法 getHttpMethod() 等。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public interface IRequest {    enum HttpMethod {
        GET, POST, PUT, DELETE
    }    //... 為了減少代碼的篇幅,省略一些對(duì)本文說明不重要的片段,本文代碼可以在      //https://github.com/wecodexyz/Componentization 獲取到
    void addParams(Map<String, String> params);

    String getUrl();

    Pair<Integer, String> doRequest();    boolean isSupportCache();    void addHeader(String key, String value);

    HttpMethod getHttpMethod();  
    //... 為了減少代碼的篇幅,省略一些對(duì)本文說明不重要的片段,本文代碼可以在      //https://github.com/wecodexyz/Componentization 獲取到}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

Request

這個(gè)類是個(gè)抽象類,對(duì) IRequest 的實(shí)現(xiàn)。目前是一個(gè)簡(jiǎn)單封裝的實(shí)現(xiàn)。

RequestWrapper

這個(gè)類是一個(gè)泛型類,繼承于 Request 并對(duì)第三方請(qǐng)求庫的封裝。例如本文就是對(duì) okhttp 的封裝,而泛型 T 對(duì)象就是請(qǐng)求得到的具體數(shù)據(jù)類型。如果要對(duì)其他請(qǐng)求庫進(jìn)行封裝,就可以參考這個(gè)類的實(shí)現(xiàn)。

注意這個(gè)類封裝是純粹的網(wǎng)絡(luò)請(qǐng)求,不應(yīng)該包含業(yè)務(wù)類相關(guān)的代碼。否則無解決上文提出的三個(gè)問題。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public abstract class RequestWrapper extends Request {    //... 為了減少代碼的篇幅,省略一些對(duì)本文說明不重要的片段,本文代碼可以在      //https://github.com/wecodexyz/Componentization 獲取到
    @Override    public Pair<Integer, String> doRequest() {
        Pair<Integer, String> result = new Pair<>(ERROR_NETWORK, "");
        okhttp3.Request request = null;        if (getHttpMethod() == HttpMethod.POST) {
            request = requestBuilder().url(getUrl()).post(requestBody()).build();
        } else {
            request = requestBuilder().url(getUrlWithParams()).build();
        }        try {
            Response response = mClient.newCall(request).execute();            if (response.isSuccessful()) {
                result = new Pair<>(response.code(), response.body().string());
            } else {
                result = new Pair<>(response.code(), response.message());
            }
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }        return result;
    }    //... 為了減少代碼的篇幅,省略一些對(duì)本文說明不重要的片段,本文代碼可以在      //https://github.com/wecodexyz/Componentization 獲取到}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

關(guān)鍵的代碼是在 doRequest() 方法中,該方法實(shí)現(xiàn)了網(wǎng)絡(luò)請(qǐng)求的代碼,返回一個(gè) Pair<Integer,String>對(duì)象,該對(duì)象的 first 屬性是一個(gè)請(qǐng)求 code ,用于標(biāo)識(shí)網(wǎng)絡(luò)請(qǐng)求碼(即是網(wǎng)絡(luò)請(qǐng)求返回的200,404,301等)。而 second 就是網(wǎng)絡(luò)請(qǐng)求的數(shù)據(jù)。

BaseTextRequest

這個(gè)類就是網(wǎng)絡(luò)請(qǐng)求框架提供給業(yè)務(wù)類使用的一個(gè)接口。本文一開始就提出來 json 作為交互數(shù)據(jù)請(qǐng)求的協(xié)議。那么此類的封裝就有利于業(yè)務(wù)數(shù)據(jù)的訪問。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public abstract class BaseTextRequest<T> extends RequestWrapper {    public BaseTextRequest(Context context) {        super(context);
    }    public Flowable<T> request() {        return Flowable.fromCallable(new Callable<Pair<Integer, String>>() {
            @Override            public Pair<Integer, String> call() throws Exception {
                Pair<Integer, String> result = doRequest();                return result;
            }
        }).flatMap(new Function<Pair<Integer, String>, Publisher<T>>() {
            @Override            public Publisher<T> apply(@NonNull Pair<Integer, String> pair) throws Exception {                if (isSuccessful(pair.first)) {                    return Flowable.just(onRequestFinish(pair.second));
                }                return Flowable.just(onRequestError(pair.first, pair.second));
            }
        });

    }

    @Override    public boolean isSupportCache() {        return true;
    }    protected abstract T onRequestFinish(String result);    protected abstract T onRequestError(int code, String message);
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

由于請(qǐng)求網(wǎng)絡(luò)是耗時(shí)的操作,rxjava2 來實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求異步操作。 request 是對(duì) RequestWrapper.doRequest() 方法的封裝,并得到一個(gè) Flowable 對(duì)象。同時(shí)定義了 onRequestFinish() 和 onRequestError() 兩個(gè)方法。

這兩個(gè)方法就是具體業(yè)務(wù)類要處理的邏輯。

SimpleTextRequest

假設(shè)有一個(gè)請(qǐng)求業(yè)務(wù)數(shù)據(jù)接口,返回?cái)?shù)據(jù)是一個(gè)字符串。那么我們使用我們的框架就是這樣來使用。本文例子是請(qǐng)求我們項(xiàng)目中的 README.md 的內(nèi)容。用起來非常簡(jiǎn)單,只要繼承于 BaseTextRequest,并實(shí)現(xiàn) getUrl() 、 onRequestFinish()onRequestError()、 getHttpMethod() 這幾個(gè)方法。

注意嚴(yán)格來說這是一個(gè)業(yè)務(wù)類,所以是不應(yīng)該放在 core 目錄下的。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

public class SimpleTextRequest extends BaseTextRequest<String> {    public SimpleTextRequest(Context context, Map<String, String> params) {        super(context);
        addParams(params);
    }

    @Override    public String getUrl() {        return "https://raw.githubusercontent.com/wecodexyz/Componentization/master/README.md";
    }

    @Override    public HttpMethod getHttpMethod() {        return HttpMethod.GET;
    }

    @Override    protected String onRequestFinish(String result) {          //這里可以實(shí)現(xiàn)對(duì) json 數(shù)據(jù)的解析,例如使用 JSONObject 
          //對(duì)象解析具體的業(yè)務(wù)
        return result;
    }

    @Override    protected String onRequestError(int code, String message) {        return message;
    }
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

測(cè)試請(qǐng)求框架

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

request = new SimpleTextRequest(this, null);
        request.request()
                .subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<String>() {
                    @Override                    public void accept(@NonNull String s) throws Exception {
                        textView.setText(s);                          //這里返回接口請(qǐng)求的數(shù)據(jù)                    }
                }, new Consumer<Throwable>() {
                    @Override                    public void accept(@NonNull Throwable throwable) throws Exception {
                        textView.setText(throwable.getMessage());
                    }
                });

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

本文運(yùn)行的結(jié)果

 

項(xiàng)目地址:https://github.com/wecodexyz/Componentization

微信關(guān)注我們,可以獲取更多

http://www.cnblogs.com/angrycode/p/7149921.html