我们在项目中或许会碰见各种各样的网络请求工具,这些工具都是基于网络框架的api上加以封装,这时我们使用时大大便利了----减少了代码重复问题、可以直接在回调中更新UI,框架更新(框架层修改)我们不必修改应用层的代码等等。因此学会二次封装网络框架,不仅可以使我们看懂别人的代码,更利于个人的进步。本文就详细讲解下Okhttp的简单封装。

写在前面

在总结之前先啰嗦几句:
1、由于okhttp源码中使用Builder模式较多。我们有必要了解下,
2、okhttp淡忘了可以重温下。
3、我们最好熟悉下我们框架的几个重要api及其作用,以及我们要实现的功能(如下图)
4、本文为封装提供了简单的实现,像企业中那种完善的网络框架,还需我们根据自己的业务不断完善吧。

Okhttp的简单封装

Okhttp的简单封装

封装

分析:
通过uml图我们可以知道okhttp可分成不同的小块(request,callback…),分别完成不同的工作,于是我们也可以按照不同的模块开始封装。
Okhttp的简单封装

1 request模块

RequestParams请求参数处理:

package com.example.administrator.okhttputil.net.request;

import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Create by SunnyDay on 2019/03/04
 *
 * @function 封装所有的请求参数到hashMap中。
 * 友情链接:
 * 1 map集合遍历参考 https://www.cnblogs.com/blest-future/p/4628871.html
 * 2 匿名内部类,非静态代码块 使用参考 https://blog.csdn.net/luman1991/article/details/53034602#commentBox
 * https://www.cnblogs.com/chenssy/p/3390871.html
 * https://zhangbo-peipei-163-com.iteye.com/blog/2023001
 * (具体参考 查找资料 java 匿名内部类的写法 ,java相关属性参数初始化顺序)
 */
public class RequestParams {
    public ConcurrentHashMap<String, String> urlParams = new ConcurrentHashMap<>();
    public ConcurrentHashMap<String, Object> fileParams = new ConcurrentHashMap<>();

    /**
     * @param source map类型参数
     * @function 构造器 吧用户传来map类型的参数存入我们的集合
     */
    public RequestParams(Map<String, String> source) {
        if (source != null) {
            //遍历key value集合
            for (Map.Entry<String, String> entry : source.entrySet()) {
                put(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * @function 构造器 接收 String类型的key value 请求参数 存入集合
     */
    public RequestParams(final String key, final String value) {
        // 参考 类注释的友情链接
        this(new HashMap<String, String>() {
            {
                put(key, value);
            }
        });


    }

    /**
     * @function 空请求参数
     */
    public RequestParams() {
        this((Map<String, String>) null);
    }

    /**
     * @param key   请求参数key
     * @param value 请求参数value
     * @function key value 放入hashmap
     */
    public void put(String key, String value) {
        if (key != null && value != null) {
            urlParams.put(key, value);
        }
    }

    /**
     * @param key    请求参数key
     * @param object 请求参数obj
     * @function 吧请求参数存入集合
     */
    public void put(String key, Object object) throws FileNotFoundException {
        if (key != null) {
            fileParams.put(key, object);
        }
    }

    /**
     * @funtion 判断是否有请求参数
     */
    public boolean hasParams() {
        if (urlParams.size() > 0 || fileParams.size() > 0) {
            return true;
        }
        return false;
    }

}

我们自己的CommonRequest

package com.example.administrator.okhttputil.net.request;
import java.util.Map;

import okhttp3.FormBody;
import okhttp3.Request;

/**
 * Create by SunnyDay on 2019/03/04
 *
 * @function 接收请求参数 为我们生成Request对象
 */
public class CommonRequest {
    /**
     * @function post 请求
     * @param url    url
     * @param params 请求参数
     *
     */
    public static Request createPostRequest(String url, RequestParams params) {
        FormBody.Builder builder = new FormBody.Builder();
        // 吧请求内容添加到请求体中
        for (Map.Entry<String, String> entry : params.urlParams.entrySet()) {
            builder.add(entry.getKey(), entry.getValue());
        }
        // 构建请求体
        FormBody formBody = builder.build();
         // 返回封装的Request请求
        return new Request.Builder().post(formBody).build();
    }

    /**
     * @function get 请求
     * @param url    url
     * @param params 请求参数
     *     通过url+请求参数的拼接 成我们的get请求url,在生成Request请求
     *
     *               get url 的方式  域名 ? key = value & key = value ......
     *   有参数就拼接url再请求, 没有参数就使用给的拼接好的url
     */
    public static Request createGetRequest(String url, RequestParams params){
        StringBuilder sb = new StringBuilder(url).append("?");
        if (params!=null){
            for (Map.Entry<String, String> entry : params.urlParams.entrySet()) {
               sb.append(entry.getKey())
                       .append("=")
                       .append(entry.getValue())
                       .append("&");
            }
            String disposedUrl = sb.toString().substring(sb.length()-1);// 去掉最后一个多余的&字符串
            return new Request.Builder()
                    .url(disposedUrl)
                    .get()
                    .build();
        }else{
            return new Request.Builder()
                    .url(url)
                    .get()
                    .build();
        }

    }
}

2 请求客户端
package com.example.administrator.okhttputil.net;

import com.example.administrator.okhttputil.net.https.HttpsUtils;
import com.example.administrator.okhttputil.net.response.CommonJsonCallback;

import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;

/**
 * Create by SunnyDay on 2019/03/04
 *
 * @function 请求的发送 请求参数的配置  https的支持
 */
public class CommonOkHttpClient {
    private static final int TIME_OUT = 30;// 超时
    private static OkHttpClient okHttpClient;

    // client的一些参数配置   在类第一次加载时完成
    static {
        OkHttpClient.Builder okhttpBuilder = new OkHttpClient.Builder();
        okhttpBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS);//连接超时
        okhttpBuilder.readTimeout(TIME_OUT, TimeUnit.SECONDS);//读超时
        okhttpBuilder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);//写超时
        okhttpBuilder.followRedirects(true);// 允许重定向

        // 添加对所有https的支持
        okhttpBuilder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true; //true: 支持任意类型的https(无论官方购买的https,或者是自己生成的https请求)
            }
        });
        okhttpBuilder.sslSocketFactory(HttpsUtils.getSslSorcetFactory());
        // 生成client对象
        okHttpClient = okhttpBuilder.build();
    }

    /**
     * 发送具体的http/https请求
     * @param request
     * @param commCallback
     * @return call
     *
     * */
    public static Call sendRequest(Request request, CommonJsonCallback commCallback) {
        Call call = okHttpClient.newCall(request);
        call.enqueue((Callback) commCallback);
        return call;
    }
}

使用的工具类

package com.example.administrator.okhttputil.net.https;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

/**
 * Create by SunnyDay on 2019/03/05
 * <p>
 * https 相关知识 参考  张鸿洋 Android Https相关完全解析 当OkHttp遇到Https
 * 友情链接:https://blog.csdn.net/lmj623565791/article/details/48129405
 */
public class HttpsUtils {
    /**
     * trust all https point
     */
    public static SSLSocketFactory getSslSorcetFactory(){
        // 1 生成信任管理器类
        X509TrustManager manager = new X509TrustManager() {
            //  所有的重写方法空实现即可
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
        // 2 创建加密上下文
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");// 协议类型与服务器保持一致
            X509TrustManager[] xTrustArray = new X509TrustManager[]{manager};
            sslContext.init(null, xTrustArray, new SecureRandom());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sslContext.getSocketFactory();
    }
}

3 回调
package com.example.administrator.okhttputil.net.response;

import android.os.Handler;
import android.os.Looper;

import com.example.administrator.okhttputil.net.exception.OkHttpException;
import com.example.administrator.okhttputil.net.listener.DisposeDataHandle;
import com.example.administrator.okhttputil.net.listener.DisposeDataListener;
import com.google.gson.Gson;

import org.json.JSONObject;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

/**
 * Create by SunnyDay on 2019/03/05
 */
public class CommonJsonCallback implements Callback {

    /**
     * 自定义类型异常
     */
    protected final int NETWORK_ERROR = -1;
    protected final int JSON_ERROR = -2;
    protected final int OTHER_ERROR = -3;


    private Handler mDeliveryHandler; //进行消息转发
    private DisposeDataListener mlistener;
    private Class<?> mClass;

    public CommonJsonCallback(DisposeDataHandle handle) {
        this.mlistener = handle.listener;
        this.mClass = handle.mClass;
        this.mDeliveryHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 请求失败处理
     */
    @Override
    public void onFailure(Call call, final IOException e) {
        mDeliveryHandler.post(new Runnable() {
            @Override
            public void run() {
                mlistener.onFailure(new OkHttpException(NETWORK_ERROR, e.getMessage()));
            }
        });
    }

    /**
     * 请求成功回调
     */
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        final String result = response.body().string();// json 类型 为String就行
        mDeliveryHandler.post(new Runnable() {
            @Override
            public void run() {
                handleResponse(result);
            }
        });
    }

    /**
     * 处理json数据
     *
     * @param responseObj json字符串
     */
    private void handleResponse(String responseObj) {
        // 判空处理
        if (responseObj == null && responseObj.trim().equals("")) {
            mlistener.onFailure(new OkHttpException(NETWORK_ERROR, "net work error"));
            return;
        }
        try {
            if (mClass == null) {
                // 用户不想让我们处理json,直接回传给用户
                mlistener.onSuccess(responseObj);
            } else {
                // 我们处理把json转化为实体
                Gson gson = new Gson();
                Object obj = gson.fromJson(responseObj, mClass);
                if (obj != null) {
                    mlistener.onSuccess(obj);
                } else {
                    // 不是合法的json
                    mlistener.onFailure(new OkHttpException(JSON_ERROR, "illegal json"));
                }
            }
        } catch (Exception e) {
            mlistener.onFailure(new OkHttpException(OTHER_ERROR, e.getMessage()));
        }
    }
}

4 回调使用到的监听处理

监听

package com.example.administrator.okhttputil.net.listener;

/**
 * Create by SunnyDay on 2019/03/05
 */
public interface DisposeDataListener {
    /**
     * 请求成功回调事件处理
     */
    void onSuccess(Object responseObj);

    /**
     * 请求失败回调事件处理
     */
    void onFailure(Object reasonObj);
}


处理

package com.example.administrator.okhttputil.net.listener;

/**
 * Create by SunnyDay on 2019/03/05
 */
public class DisposeDataHandle<T> {
    public DisposeDataListener listener = null;
    public Class<?> mClass = null;

    public DisposeDataHandle(DisposeDataListener listener) {
        this.listener = listener;
    }


    public DisposeDataHandle(DisposeDataListener listener,Class<?> mClass ) {
        this.listener = listener;
        this.mClass = mClass;

    }
}

异常

 package com.example.administrator.okhttputil.net.exception;

/**
 * Create by SunnyDay on 2019/03/05
 */
public class OkHttpException extends Exception {

    private int ecode;// 服务端返回码
    private Object emsg;// 服务端返回信息

    public OkHttpException(int ecode, Object emsg) {
        this.ecode = ecode;
        this.emsg = emsg;
    }

    public int getEcode() {
        return ecode;
    }

    public void setEcode(int ecode) {
        this.ecode = ecode;
    }

    public Object getEmsg() {
        return emsg;
    }

    public void setEmsg(Object emsg) {
        this.emsg = emsg;
    }
}


callback中测试

更新UI

Okhttp的简单封装

直接获得实体测试

 @Override
                    public void onSuccess(Object responseObj) {
                        TestBean testBean = (TestBean) responseObj;
                        Log.i(TAG, "onSuccess: "+testBean.getButtonName());
                        //okhttputil I/abc: onSuccess: 我知道了
                    }

测试json

小结

这样简单的封装就完成了,作者在学习时在里面添加了不少注释,补充资料,想必大家都能看懂,如果实在看不懂的话可以下载源码慢慢分析,或者联系作者,作者有视屏资料哦,哈哈溜了。。。。

源码下载

The end

相关文章: