在 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()
等。
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 獲取到}
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è)問題。
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 獲取到}
關(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ù)的訪問。
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); }
由于請(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 目錄下的。
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; } }
測(cè)試請(qǐng)求框架
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()); } });
本文運(yùn)行的結(jié)果
項(xiàng)目地址:https://github.com/wecodexyz/Componentization
微信關(guān)注我們,可以獲取更多
http://www.cnblogs.com/angrycode/p/7149921.html