基于okhttp封装网络库 (1)

基于okhttp封装网络库 (1)

前言:在前篇文章中我已经总结了 okhttp 库的特性,这次就来讲对该库封装思路,当然是在熟悉 okhttp 基本 api使用情况下。

封装思路

  1. 首先是确定协议,一般来讲公司都会采用一二级协议的格式,所以这篇是假设服务端跟客户端协定的协议为一二级协议的格式,比如:

(输入参数)

参数名称类型默认值描述
idInteger200Book ID

(输出参数)

Response类型默认值描述
codeInteger200请求状态
messageString请求成功请求信息
dataBookDetail请求二级协议
  1. 确定协议后首先确定的是 主机地址,然后确定具体的业务信息来构成 URL,一个推荐的方式是

    • URL: host + api 版本 + 业务请求地址
    • 实例: Http://api.example.com/v2/item/detail
    • 这样在 api 版本升级时或者发生改动时,旧版本的业务处理代码不需要做任何修改,直接在 URL 中修改参数就可。
  2. 下一步就是确定请求 request 报文:

  3. 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 请求实体内容来写具体的请求业务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方式,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值