简单封装okHttp网络请求框架

一、前言

      目前好像使用okHttp网络请求框架的人越来越多了,比如大家会说它使用高效,安全等很多优点。毫无疑问,本人也使用一段时间了,不过可能很多人和我一样,在使用okHttp框架之前,可能是使用android-async-http这类框架的,相比前者,在使用okHttp框架完成一次请求,还是需要写蛮多代码的,而且挺多一部分是重复的代码,所以这也就是我准备对它进行二次包装的原因。(另外,本文是在熟悉或了解okHttp写的,如果是想对okHttp框架使用说明了解的,可以阅读下这篇文章: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html)

二、概述

      二次封装okHttp网络请求框架,目的是简化一些代码的重复编写,使得整个使用过程更加简单易操作,那么从我们最容易变化的几个地方来进行封装:
1)、请求的参数
2)、API的调用
3)、回调接口

三、参数的封装

       先来看下okHttp在传递普通类型参数和文件上传的时候,参数是如何接收的。
       A、普通参数     
RequestBody formBody = new FormEncodingBuilder()
        .add("name", "姓名")
        .add("age", 26)
        .build();
Request request = new Request.Builder()
        .type(MultipartBuilder.FORM)
    Request request =  = new Request.Builder()
       虽然,有FormEncodingBuilder和MultipartBuilder这两个类
        .url("http://www.baidu.com")
        .post(formBody)
        .build();
      B、文件类
RequestBody requestBody = new MultipartBuilder()
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"file\""),
            RequestBody.create(MEDIA_TYPE_PNG, new File("upload.png")))
        .build();
        .url("http://www.baidu.com")
        .post(requestBody)
        .build();
      虽然,已经提供了 FormEncodingBuilder和 MultipartBuilder这两个类来去组装参数,但后面写那么一大串,感觉还是有些繁琐,其实如果我们可以类似前面的那些框架一样,把参数都封装在一个Map这样的集合中,不就简单了么。
public class RequestParams {

    private static final MediaType MEDIA_TYPE_IMAGE  = MediaType.parse("image/png");
    private static final MediaType MEDIA_TYPE_STREAM = MediaType.parse("text/x-markdown; charset=utf-8");
    private static final MediaType MEDIA_TYPE_STRING = MediaType.parse("text/plain;charset=utf-8");

    protected ConcurrentHashMap<String, String> urlParams;
    protected ConcurrentHashMap<String, FileWrapper> fileParams;

    public RequestParams(){
        init();
    }

    public RequestParams(Map<String, String> source) {
        init();

        for(Map.Entry<String, String> entry : source.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    public RequestParams(String key, String value) {
        init();
        put(key, value);
    }

    public RequestParams(Object... keysAndValues) {
        init();
        int len = keysAndValues.length;
        if (len % 2 != 0)
            throw new IllegalArgumentException("Supplied arguments must be even");
        for (int i = 0; i < len; i += 2) {
            String key = String.valueOf(keysAndValues[i]);
            String val = String.valueOf(keysAndValues[i + 1]);
            put(key, val);
        }
    }

    private void init(){
        urlParams = new ConcurrentHashMap<String, String>();
        fileParams = new ConcurrentHashMap<String, FileWrapper>();
    }

    public void put(String key, String value){
        if(key != null && value != null) {
            urlParams.put(key, value);
        }
    }

    public void put(String key,int value){
        if(key != null && String.valueOf(value) != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }

    public void put(String key,float value){
        if(key != null && String.valueOf(value) != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }

    public void put(String key,double value){
        if(key != null && String.valueOf(value) != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }

    public void put(String key,boolean value){
        if(key != null && String.valueOf(value) != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }

    public void put(String key, File file) throws FileNotFoundException {
        put(key, new FileWrapper(file, FileWrapperType.FILE));
    }

    public void put(String key, byte[] bytes) throws FileNotFoundException {
        put(key, new FileWrapper(bytes, FileWrapperType.BYTE));
    }

    public void put(String key, FileWrapper fileWrapper){
        if(key != null && fileWrapper != null){
            fileParams.put(key,fileWrapper);
        }
    }

    public void remove(String key){
        urlParams.remove(key);
        fileParams.remove(key);
    }

    private enum FileWrapperType{
        FILE(0,"文件"),
        BYTE(1,"字节流");

        protected int code;
        protected String desc;
        FileWrapperType(int code,String desc){
            this.code = code;
            this.desc = desc;
        }
    }

    private static class FileWrapper {
        public File file = null;
        public byte[] fileBytes = null;
        public FileWrapperType contentType;

        public FileWrapper(File file,FileWrapperType contentType) {
            this.file = file;
            this.contentType = contentType;
        }

        public FileWrapper(byte[] fileBytes,FileWrapperType contentType) {
            this.fileBytes = fileBytes;
            this.contentType = contentType;
        }
    }

    public String getParamString() {
        if (!urlParams.isEmpty()) {
            StringBuffer sb = null;
            for(ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
                if (sb == null) {
                    sb = new StringBuffer();
                    sb.append("?");
                } else {
                    sb.append("&");
                }
                sb.append(entry.getKey());
                sb.append("=");
                sb.append(entry.getValue());
            }
            return sb.toString();
        }
        return null;
    }

    public RequestBody paramsBody(){

        //处理文件参数
        if(!fileParams.isEmpty()){
            MultipartBuilder multipartBuilder = new MultipartBuilder();
            multipartBuilder.type(MultipartBuilder.FORM);

            if(!urlParams.isEmpty()) {
                for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
                    multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());
                }
            }

            for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
                FileWrapper value = entry.getValue();
                switch (value.contentType){
                    case FILE:
                        multipartBuilder.addFormDataPart(entry.getKey(), value.file.getName(),
                                RequestBody.create(MEDIA_TYPE_IMAGE, value.file));
                        break;
                    case BYTE:
                        multipartBuilder.addFormDataPart(entry.getKey(), "test.jpg",
                                RequestBody.create(MEDIA_TYPE_STREAM, value.fileBytes));
                        break;
                }
            }//end for

            return multipartBuilder.build();
        }//end if

        //处理普通参数
        if(!urlParams.isEmpty()) {
            FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
            for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
                formEncodingBuilder.add(entry.getKey(), entry.getValue());
            }
            return formEncodingBuilder.build();
        }

        return null;
    }

}
上面定义的RequestParams这个类就是通过key-value这么一种方式专门包装参数的,value接收基本数据类型和File文件类型以及字符流。这样,普通的使用者就更容易上手了。

四、Http接口的封装

okHttp作为一个流行的框架,当然是提供了很多种请求的方式,但往往我们在项目开发中,用得最多了还是只有get和post这两种方式,所以我这里也只提炼这两种方式的接口就可以。
public static <T> void get(String url,final ResultCallBack<T> resultCallBack){
    getInstance().handlerRequest(getInstance().buildGetRequest(url, null), resultCallBack);
}

public static <T> void get(String url,RequestParams params,final ResultCallBack<T> resultCallBack){
    getInstance().handlerRequest(getInstance().buildGetRequest(url, params), resultCallBack);
}

public static void post(String url){
    getInstance().handlerRequest(getInstance().buildPostRequest(url, null), null);
}

public static <T> void post(String url,final ResultCallBack<T> resultCallBack){
    getInstance().handlerRequest(getInstance().buildPostRequest(url, null), resultCallBack);
}

public static <T> void post(String url,RequestParams params,final ResultCallBack<T> resultCallBack){
    getInstance().handlerRequest(getInstance().buildPostRequest(url, params), resultCallBack);
}
/**
 * 获取基础请求参数,对于一些公共的参数,我们可以统一写在这里
 * @param params
 * @return
 */
private RequestParams getBaseParams(RequestParams params){
    if(params == null){
        params = new RequestParams();
    }
    params.put("appKey", AccountPreference.getInstance().getAppKey(""));
    params.put("deviceType", "Android");
    params.put("pageSize", TDevice.getPageSize());
    params.put("v", PackageUtils.getCurrentVersionName(BaseApplication.context()));
    return params;
}

/**
 * 组装GET请求
 * @param url
 * @param params
 * @return
 */
private Request buildGetRequest(String url,RequestParams params){
    Request.Builder reqBuilder = new Request.Builder();
    params = getBaseParams(params);
    String paramsStr = params.getParamString();
    if(paramsStr != null){
        url += paramsStr;
    }
    LogUtils.e("GET方法请求:" + url);
    reqBuilder.url(url);
    return reqBuilder.build();
}

/**
 * 组装POST请求
 * @param url
 * @param params
 * @return
 */
private Request buildPostRequest(String url,RequestParams params){
    Request.Builder reqBuilder = new Request.Builder();
    reqBuilder.url(url);
    params = getBaseParams(params);
    LogUtils.e("POST方法请求:" + url+params.getParamString());
    RequestBody requestBody = params.paramsBody();
    if(requestBody != null){
        reqBuilder.post(requestBody);
    }
    return reqBuilder.build();
}

五、CallBack回调的封装

       由于这里我们请求回来的数据,通常都是希望按开发者需要的结果返回的,而除了文件下载之外,普通网络请求回来的数据,我们都是使用json的格式进行传输的,我们的接口使用的是泛型,即可接收任意类型的数据,所以这里需要对我们回来的json数据进行格式化。格式化库我采用的是fastjson,未接触过的可以网上搜索下。要想格式化出是我们期待的数据类型,得先知道类型,所以这里重新定义了一个回调类
public abstract class ResultCallBack<T> {

    Type mType;

    static Type getSuperclassTypeParameter(Class<?> subclass) {
        Type superclass = subclass.getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        return ((java.lang.reflect.ParameterizedType)superclass).getActualTypeArguments()[0];
    }

    public ResultCallBack(){
        mType = getSuperclassTypeParameter(getClass());
    }

    public void onPreExecute(){};

    public void onSuccess(T t){};

    public void onFailure(Request request, AppException e){};

}
结合定义的ResultCallBack类,最终处理Http的请求结果
/**
 * 处理HTTP请求
 * @param request
 * @param resultCallBack
 * @param <T>
 */
private <T> void handlerRequest(Request request,final ResultCallBack<T> resultCallBack){
    //判断当前网络情况
    if(!NetworkUtils.isNetworkAvailable(BaseApplication.context())){
        BaseApplication.showToast(R.string.netword_error);
        if (resultCallBack != null) {
            resultCallBack.onFailure(request, AppException.getAppExceptionHandler());
        }
        return;
    }
    if(resultCallBack != null){
        resultCallBack.onPreExecute();
    }
    Call call = client.newCall(request);
    call.enqueue(new Callback() {
        @Override
        public void onFailure(final Request request, final IOException e) {
            LogUtils.e("请求失败打印:"+e.toString());
            uiHandler.post(new Runnable() {
                @Override
                public void run() {
                    if (resultCallBack != null) {
                        resultCallBack.onFailure(request, AppException.io(e));
                    }
                }
            });
        }

        @Override
        public void onResponse(final Response response) {
            try {
                if(response.isSuccessful()){
                    final String strBody = response.body().string();
                    LogUtils.e("请求结果打印:" + strBody);
                    if(StringUtils.isVoid(strBody)){
                        this.onFailure(response.request(),new IOException("http request data empty"));
                    }else {
                        final JSONObject json = JSON.parseObject(strBody);
                        //可以根据项目的约定来统一处理一些全局的请求码
                        if(json.getIntValue("result") == -5){
                            this.onFailure(response.request(),new IOException("http request time out"));
                            uiHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    EventBus.getDefault().post(true, BusTags.LoginTimeout);
                                }
                            });
                            return;
                        }
                        if (resultCallBack != null) {
                            if (resultCallBack.mType == String.class) {
                                uiHandler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        resultCallBack.onSuccess((T) strBody);
                                    }
                                });
                            } else {
                                final T t = JSON.parseObject(strBody, resultCallBack.mType);
                                uiHandler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        resultCallBack.onSuccess(t);
                                    }
                                });
                            }
                        }
                    }
                }else{
                    this.onFailure(response.request(),new IOException("http request failure"));
                }
            }catch (final Exception e){
                this.onFailure(response.request(),new IOException(e.getCause()));
            }
        }
    });
}
上面不管是在 onFailure ,还是 onResponse的回调方法里面,我们是通过handle传结果回去的,这是因为okHttp的回调是不是在UI主线程的,所以这里可以根据我们自己项目的需求来去做不同的反应。

六、实践使用
RequestParams params = new RequestParams();
params.put("name", "张三");//普通字符串类型
params.put("age", 26);//普通数据类型
params.put("file", new File("sdcard/upload.png"));//文件类型
String partUrl = "user/save";
HttpApi.getInstance().post(partUrl, params, new ResultCallBack<UserResult>(){
    @Override
    public void onSuccess(final UserResult bean) {
        super.onSuccess(bean);
        //处理回来的结果
    }

    @Override
    public void onPreExecute() {
        super.onPreExecute();
       //请求前的ui工作
    }

    @Override
    public void onFailure(Request request, AppException e) {
        super.onFailure(request, e);
        //请求失败处理
    }
});
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值