1. 程式人生 > >App 組件化/模塊化之路——如何封裝網絡請求框架

App 組件化/模塊化之路——如何封裝網絡請求框架

response map 一般來說 extend www 請求方法 client tpm sim

App 組件化/模塊化之路——如何封裝網絡請求框架

在 App 開發中網絡請求是每個開發者必備的開發庫,也出現了許多優秀開源的網絡請求庫。例如

  • okhttp
  • retrofit
  • android-async-http

這些網絡請求庫很大程度上提高程序猿的編碼效率。但是隨著業務的發展,App 變得越來越大,我們將這些網絡請求庫加入到項目中直接使用,對我們業務類的入侵是非常強的。如果要進行業務分離時,這些網絡請求代碼將是一個阻止我們進一步工作的絆腳石。對開發者來說是非常痛苦的。

因此我們構建的網絡請求框架要可以解決以下問題:

  • 分離業務與網絡請求代碼
  • 網絡庫可以很容易的被替換
  • 網絡庫可以很方便的復用

所以在 App 組件化/模塊化開發架構思路 一文中,我們把網絡請求作為內核層的一個組件。

封裝第三方網絡請求接口

一般來說,目前絕大部分 App 的數據請求都是使用 HTTP 協議,而數據交換的協議使用 json 格式。因此可以封裝一個通用的請求接口。(當然還有其他一些協議,例如微信的 mars ,但是封裝的思路是一致的,本文為了簡單說明,暫時使用通用網絡請求框架,不排除以後會對 mars 的封裝)

首先預覽一下框架結構

技術分享

IRequest

這個類封裝了網絡請求的通用接口,定義請求接口 doRequest() 、獲取請求連接 getUrl() 、獲取請求方法 getHttpMethod()

等。

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

Request

這個類是個抽象類,對 IRequest 的實現。目前是一個簡單封裝的實現。

RequestWrapper

這個類是一個泛型類,繼承於 Request 並對第三方請求庫的封裝。例如本文就是對 okhttp 的封裝,而泛型 T 對象就是請求得到的具體數據類型。如果要對其他請求庫進行封裝,就可以參考這個類的實現。

註意這個類封裝是純粹的網絡請求,不應該包含業務類相關的代碼。否則無解決上文提出的三個問題。

public abstract class RequestWrapper extends Request {

    //... 為了減少代碼的篇幅,省略一些對本文說明不重要的片段,本文代碼可以在
      //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;
    }

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


}

關鍵的代碼是在 doRequest() 方法中,該方法實現了網絡請求的代碼,返回一個 Pair<Integer,String>對象,該對象的 first 屬性是一個請求 code ,用於標識網絡請求碼(即是網絡請求返回的200,404,301等)。而 second 就是網絡請求的數據。

BaseTextRequest

這個類就是網絡請求框架提供給業務類使用的一個接口。本文一開始就提出來 json 作為交互數據請求的協議。那麽此類的封裝就有利於業務數據的訪問。

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);
}

由於請求網絡是耗時的操作,rxjava2 來實現網絡請求異步操作。 request 是對 RequestWrapper.doRequest() 方法的封裝,並得到一個 Flowable 對象。同時定義了 onRequestFinish()onRequestError() 兩個方法。

這兩個方法就是具體業務類要處理的邏輯。

SimpleTextRequest

假設有一個請求業務數據接口,返回數據是一個字符串。那麽我們使用我們的框架就是這樣來使用。本文例子是請求我們項目中的 README.md 的內容。用起來非常簡單,只要繼承於 BaseTextRequest,並實現 getUrl()onRequestFinish() onRequestError()getHttpMethod() 這幾個方法。

註意嚴格來說這是一個業務類,所以是不應該放在 core 目錄下的。

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) {
          //這裏可以實現對 json 數據的解析,例如使用 JSONObject 
          //對象解析具體的業務
        return result;
    }

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

測試請求框架

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);
                          //這裏返回接口請求的數據
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(@NonNull Throwable throwable) throws Exception {
                        textView.setText(throwable.getMessage());
                    }
                });

本文運行的結果

技術分享

項目地址:https://github.com/wecodexyz/Componentization

微信關註我們,可以獲取更多

技術分享

App 組件化/模塊化之路——如何封裝網絡請求框架