App 组件化/模块化之路——如何封装网络请求框架
在 App 开发中网络请求是每个开发者必备的开发库,也出现了许多优秀开源的网络请求库。例如
这些网络请求库很大程度上提高程序猿的编码效率。但是随着业务的发展,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
微信关注我们,可以获取更多