App 組件化/模塊化之路——如何封裝網絡請求框架
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 組件化/模塊化之路——如何封裝網絡請求框架