基于okhttp封装网络库 (1)
前言:在前篇文章中我已经总结了 okhttp 库的特性,这次就来讲对该库封装思路,当然是在熟悉 okhttp 基本 api使用情况下。
封装思路
首先是确定协议,一般来讲公司都会采用一二级协议的格式,所以这篇是假设服务端跟客户端协定的协议为一二级协议的格式,比如:
(输入参数)
参数名称类型默认值描述
idInteger200Book ID
(输出参数)
Response类型默认值描述
codeInteger200请求状态messageString请求成功请求信息dataBookDetail请求二级协议
确定协议后首先确定的是 主机地址,然后确定具体的业务信息来构成 URL,一个推荐的方式是
URL: host + api 版本 + 业务请求地址实例: Http://api.example.com/v2/item/detail 这样在 api 版本升级时或者发生改动时,旧版本的业务处理代码不需要做任何修改,直接在 URL 中修改参数就可。
下一步就是确定请求 request 报文:
请求类型:GET / Post
GET:Accept
Http://api.example.com/v2/item/detail/id=xxxPost :Content-Type :
application/x-www-form-urlencoded:id=xxxapplication/json:{“id”,xxx}
而 response body 已经在上列的拖中展示出来,在获得 data 后这时候我们假设 data 字段是 json 格式的,然后将 json 转化成 Bean,也就是我们 java 中的实体类。
总结:为完成上述的要求也同时为了代码重用和避免代码混乱,我们需要一个通用的模式,下面就是一个简单却又完整的 BaseRequest 抽象类来完成上述要求。
BaseRequest
public abstract class BaseRequest<T> {
protected HashMap<String, String> mBodyMap =
new HashMap<String, String>();
protected HashMap<String, String> mQueryMap =
new HashMap<String, String>();
private OkHttpClient mClient;
private Call mCall;
public BaseRequest(OkHttpClient httpClient) {
this.mClient = httpClient;
}
protected String
getHost() {
return HttpConfig.HOST;
}
public abstract String
getApi();
public abstract int getHttpMethod();
public abstract Class<T>
getModelClass();
public abstract MediaType
getMediaType();
protected abstract String
getRequestString();
public String
getUrl() {
if (HttpMethod.GET == getHttpMethod()) {
String queryString = getQueryString();
if (!TextUtils.isEmpty(queryString)) {
return String.format(
"%s%s?%s", getHost(), getApi(), queryString);
}
}
return String.format(
"%s%s", getHost(), getApi());
}
protected Request
buildRequest() {
RequestBody body =
null;
Request.Builder builder =
new Request.Builder();
String strRequest = getRequestString();
switch (getHttpMethod()) {
case HttpMethod.GET:
builder.url(getUrl());
break;
case HttpMethod.POST:
body = RequestBody.create(getMediaType(),strRequest);
builder.url(getUrl()).post(body);
break;
default:
break;
}
return builder.build();
}
public String
getQueryString() {
if (mQueryMap !=
null && mQueryMap.size() >
0) {
return encodeParameters(mQueryMap,
"utf-8");
}
return null;
}
protected String
encodeParameters(Map<String, String>
params, String paramsEncoding) {
try {
if (
params !=
null &&
params.size() >
0) {
StringBuilder encodedParams =
new StringBuilder();
int index =
0;
for (Map.Entry<String, String> entry :
params.entrySet()) {
if (TextUtils.isEmpty(entry.getKey()) || TextUtils.isEmpty(entry.getValue())) {
continue;
}
if (index >
0) encodedParams.append(
'&');
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append(
'=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
index++;
}
return encodedParams.toString();
}
else {
return null;
}
}
catch (UnsupportedEncodingException uee) {
throw new RuntimeException(
"Encoding not supported: " + paramsEncoding, uee);
}
}
}
public class HttpMethod {
public static final
int POST =
1;
public static final
int GET =
2;
}
public class HttpConfig {
public static final String HOST =
"http://1.1.1.1";
}
public class HttpResponse<T> {
public Integer code;
public String message;
public T data;
public HttpResponse(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public HttpResponse() {
}
}
以上就描述了一个基本的 GET 和 POST 方法,然后开始同步请求封装
同步请求封装
/***
* 同步调用
*
* @return
* @throws Exception
*/
public HttpResponse<T>
execute()
throws IOException {
Response response =
this.mClient.newCall(buildRequest()).execute();
int code = response.code();
if (code ==
200) {
ResponseBody body = response.body();
return getResponse(body);
}
else {
return null;
}
}
private HttpResponse<T>
getResponse(ResponseBody body)
throws IOException {
String strBody = body.string();
JSONObject json = JSON.parseObject(strBody);
HttpResponse<T> httpResponse =
new HttpResponse<T>();
httpResponse.code = json.getInteger(
"code");
httpResponse.message = json.getString(
"message");
String strData = json.getString(
"data");
Object data = JSONObject.parseObject(strData, getModelClass());
httpResponse.data = (T) data;
return httpResponse;
}
异步请求封装
private ExecutorDelivery mDelivery;
public boolean
enqueue(final HttpCallback<T> callback) {
if (mCall ==
null) {
mCall = mClient.newCall(buildRequest());
mCall.enqueue(
new Callback() {
@Override
public void onFailure(Call call, IOException e) {
HttpResponse response =
new HttpResponse(ResponseCode.NET_ERROR,
"网络错误",
null);
mDelivery.postResponse(callback, BaseRequest.
this, response, e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
HttpResponse<T> httpResponse =
null;
try {
httpResponse = getResponse(response.body());
mDelivery.postResponse(callback, BaseRequest.
this, httpResponse,
null);
}
catch (IOException e) {
httpResponse =
new HttpResponse(ResponseCode.NET_ERROR,
"网络错误",
null);
mDelivery.postResponse(callback, BaseRequest.
this, httpResponse, e);
}
catch (JSONException e) {
httpResponse =
new HttpResponse(ResponseCode.JSON_ERROR,
"Json解析错误",
null);
mDelivery.postResponse(callback, BaseRequest.
this, httpResponse, e);
}
}
});
}
else {
if (mCall.isCanceled() || mCall.isExecuted()) {
Log.e(
"TAG",
"cancel executed");
}
}
return true;
public interface HttpCallback<T> {
public void onResponse(BaseRequest request, Object data);
public void onFailure(BaseRequest request, Exception e,
int code, String message);
}
public interface ResponseCode {
public static final
int SUCCESS =
200;
public static final
int FAILED =
2;
public static final
int ERROR = -
1;
public static final
int NET_ERROR = -
200;
public static final
int JSON_ERROR = -
201;
}
主线程分发器 ExecutorDelivery
public class ExecutorDelivery {
/**
* Used for posting responses, typically to the main thread.
*/
private final Executor mResponsePoster;
/**
* Creates a new response delivery interface.
*
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(
final Handler handler) {
mResponsePoster =
new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
public void postResponse(HttpCallback httpCallback, BaseRequest request, HttpResponse response, Exception e) {
mResponsePoster.execute(ResponseDeliveryRunnable2.response(httpCallback, request, response, e));
}
private static class ResponseDeliveryRunnable2 implements Runnable {
private final BaseRequest request;
private final Exception e;
private HttpResponse httpResponse;
private HttpCallback callback;
private ResponseDeliveryRunnable2(HttpCallback callback, BaseRequest request, HttpResponse response, Exception e) {
this.callback = callback;
this.request = request;
this.e =
null;
this.httpResponse = response;
}
public static ExecutorDelivery.ResponseDeliveryRunnable2
response(HttpCallback modelCallback, BaseRequest request, HttpResponse response, Exception e) {
return new ExecutorDelivery.ResponseDeliveryRunnable2(modelCallback, request, response, e);
}
@SuppressWarnings(
"unchecked")
@Override
public void run() {
if (e ==
null && httpResponse.code == ResponseCode.SUCCESS) {
this.callback.onResponse(request, httpResponse.data);
}
else {
this.callback.onFailure(request, e, httpResponse.code, httpResponse.message);
}
}
}
}
HttpClientWrapper 及新的 BaseRequest 构造方法
public BaseRequest(HttpClientWrapper httpClient) {
this.mClient = httpClient.getOkHttpClient();
this.mDelivery = httpClient.getDelivery();
}
public class HttpClientWrapper {
private OkHttpClient mOkHttpClient;
private ExecutorDelivery mDelivery;
public HttpClientWrapper(Context context) {
Handler handler =
new Handler(Looper.getMainLooper());
mDelivery =
new ExecutorDelivery(handler);
mOkHttpClient =
new OkHttpClient.Builder().connectTimeout(
3, TimeUnit.SECONDS).readTimeout(
3, TimeUnit.SECONDS).writeTimeout(
3, TimeUnit.SECONDS).build();
}
public OkHttpClient
getOkHttpClient() {
return mOkHttpClient;
}
public ExecutorDelivery
getDelivery() {
return mDelivery;
}
}
总结
至此已经封装好了一个请求基类,下一步就是根据 GET/POST 请求实体内容来写具体的请求业务。