基于okhttp封装网络库 (1)

    xiaoxiao2021-03-25  145

    基于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> { //存放 POST 请求主体内容, protected 关键字是为了给子类访问到。 protected HashMap<String, String> mBodyMap = new HashMap<String, String>(); //存放 GET 请求参数 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; } //api 地址 public abstract String getApi(); //请求方式 public abstract int getHttpMethod(); //返回 Bean public abstract Class<T> getModelClass(); //请求内容 public abstract MediaType getMediaType(); //将 mBodyMap 里面的 POST 请求体转成 String 格式传给 RequestBody.create() 方法 //在子类中完成,跟 getMediaType() 组合来完成不同业务的请求需求。 protected abstract String getRequestString(); //发出请求必须要有 URL 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()); } //将我们的常规请求转换为 okhttp 中的 request 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: //TODO break; } return builder.build(); } //返回 GET 请求参数集合,如 id=xxx&&name=xxx public String getQueryString() { if (mQueryMap != null && mQueryMap.size() > 0) { return encodeParameters(mQueryMap, "utf-8"); } return null; } //将 map 键值对取出来组装成 id=xxx&&name=xxx 格式 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; } } //将服务器返回的 Json 数据解析成 HTTPResponse 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; }

    异步请求封装

    //主线程分发器,因为 okhttp 的异步调用方法发生在子线程,我们需要将结果发送回主线程 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> { /*** * 请求成功,httpcode 200,业务code == 200 */ public void onResponse(BaseRequest request, Object data); /** * @param request * @param e 异常:网络异常,json解析异常 * @param code 业务code,若httpcode为200,后两个参数有效;服务端的code, * @param message 业务message */ public void onFailure(BaseRequest request, Exception e, int code, String message); } //注意下列 code 应该在一级协议中跟服务器约定好具体数值的含义! 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; /*** * json格式错误 */ public static final int JSON_ERROR = -201; }

    主线程分发器 ExecutorDelivery

    //利用主线程创建的 Handler 来完成分发器的初始化工作。 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) { // Make an Executor that just wraps the 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(); } //写这个网络封装器的目的是将业务层和底层网络库解耦,上层业务开发者不用关心用的是哪个网络库。 //在 Application 中初始化 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 请求实体内容来写具体的请求业务。
    转载请注明原文地址: https://ju.6miu.com/read-4909.html

    最新回复(0)