对OKHttp3的简单封装

一个完整的Android项目,肯定少不了网络请求的相关类。我整理了一下自己现在项目里二次封装的网络请求类,方便以后查阅,也希望大家指正。

Http请求使用了现在主流的第三方库OKHttp3,数据是json格式的,我直接用了gson。

添加对应的依赖:

 compile 'com.squareup.okio:okio:1.11.0'
 compile 'com.squareup.okhttp3:okhttp:3.6.0'
 compile 'com.google.code.gson:gson:2.7'

整个网络请求功能主要由四个类组成:
· NetworkUtil:负责主要的网络请求及请求结果返回的处理。
· NetworkStatus:枚举类,包含了网络请求的返回结果状态
· Parameter:接口请求参数生成工具类
· NetworkCallBack:抽象类,用于http请求回调



首先说一下NetworkStatus

public enum NetworkStatus {
    STATUS_SUCCESS("0", "请求成功", null),
    STATUS_FAIL_PARAMS_ERROR("1", "请求参数异常", null),
    STATUS_FAIL_SERVICE_ERROR("2", "服务器异常", null),
    STATUS_FAIL_IO_EXCEPTION("2", "网络异常", null),
    STATUS_FAIL_SESSION_OUT("4", "登录超时", null),
    STATUS_FAIL_PARSING_ERROR("5", "数据解析出错", null);

    private String code;
    private String message;
    private String responseStr;

    NetworkStatus(String code, String message, String responseStr) {
        this.code = code;
        this.message = message;
        this.responseStr = responseStr;
    }

    public String getMessage() {
        return message;
    }

    public NetworkStatus setMessage(String message) {
        this.message = message;
        return this;
    }

    public String getCode() {
        return code;
    }

    public NetworkStatus setCode(String code) {
        this.code = code;
        return this;
    }

    public static NetworkStatus getNetworkStatusByCode(String code){
        NetworkStatus resultStatus = NetworkStatus.STATUS_FAIL_PARSING_ERROR;
        for(NetworkStatus status: values()){
            if(status.code.equals(code)){
                resultStatus = status;
                break;
            }
        }
        return resultStatus;
    }

    public String getResponseStr() {
        return responseStr;
    }

    public NetworkStatus setResponseStr(String responseStr) {
        this.responseStr = responseStr;
        return this;
    }
}

这个枚举类包含了6个常量,代表可能出现的6中网络请求结果。类本身有三个参数,其中code和message是服务器返回的状态码和信息,responseStr是json字符串。

除了这三个方法的getter、setter以外,还有一个公共方法getNetworkStatusByCode(String code)。这个方法根据服务器给的code,返回具体的枚举对象。这个方法将用于对http response的处理。



接下来是Parameter类

public class Parameter {

    private final static String DATA_FORMAT_JSON = "JSON";

    private final static String ENCRYPT_TYPE_DES = "001";

    /**
     * 数据结构类型(json、xml等等)
     */
    public String format;
    /**
     * 加密类型
     * 001:Des加密
     */
    public String encryptType;
    /**
     * 是否加密
     * 0:不加密 1:加密
     */
    public String isEncrypt;

    /**
     * 公共参数
     */
    private HashMap<String, String> head;

    private Object data;

    /**
     * 默认构造函数,初始化时默认:
     * <br>format为FORMAT_JSON
     * <br>encryptType为ENCRYPT_TYPE_DES
     * <br>isEncrypt为true
     * <br>并且在构造方法中设置公共参数
     */
    public Parameter(Context context) {
        head = new HashMap<>();
        data = new HashMap<String, String>();
        this.format = DATA_FORMAT_JSON;
        this.encryptType = ENCRYPT_TYPE_DES;
        this.isEncrypt = String.valueOf(BaseConfig.YES);
        head.put("phoneName", DeviceInfo.getInstance().model);
        head.put("phoneVersion", DeviceInfo.getInstance().osVersion);
        head.put("imei", DeviceInfo.getInstance().imei);
        head.put("softVersion", AppUtil.getAppVersionName());
        head.put("imsi", DeviceInfo.getInstance().simImsi);
        head.put("loginName", ClientApplication.getInstance().getUserInfo() == null ? "" : ClientApplication.getInstance().getUserInfo().getLoginName());
        head.put("ownship", DeviceInfo.getInstance().simCarrier);
        head.put("sk", DeviceInfo.getInstance().imei);
    }

    public void setHeadLoginName(String loginName){
        head.put("loginName", loginName);
    }

    /**
     * 生成公共参数
     *
     * @return 按照指定的数据结构类型返回相应的公共参数字符串表述
     */
    public String getHeadString() {
        if (TextUtils.isEmpty(format)) {
            return null;
        }
        String result = null;
        if (format.equals(DATA_FORMAT_JSON)) {
            try {
                JSONObject main = new JSONObject();
                for (Map.Entry<String, String> entry : head.entrySet()) {
                    main.put(entry.getKey(), entry.getValue());
                }
                result = main.toString().trim();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return result;
    }


    public void setData(Object data) {
        this.data = data;
    }


    /**
     * 生成业务参数
     *
     * @return 按照指定的数据结构类型返回相应的业务参数字符串表述
     */
    public String getDataString() {
        if (data == null) {
            return null;
        }
        String result = null;
        Gson gson = null;
        if (data instanceof Map) {
            gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization().create();
        } else {
            gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation() // 不导出实体中没有用@Expose注解的属性
//                    .serializeNulls()// 将取值为null的字段也输出到json字符串中
                    .setDateFormat("yyyy-MM-dd HH:mm:ss")// 时间转化为特定格式
                    .setPrettyPrinting() // 对json结果格式化.
                    .create();
        }
        result = gson.toJson(data);

        return result;
    }

    @Override
    public String toString() {
        return "format=" + format + "&isKey=" + isEncrypt + "&keyType=" + encryptType + "&head=" + getHeadString() + "&data=" + getDataString();
    }
}

手机端与服务器端的数据交互,一般都是通过“调用后台接口”来完成。
以自己的项目为例,接口请求格式如下:

http://ip:port/action_method.do?format=JSON&encryptType=001&isEncrypt=1&head={公共协议头}&data={业务内容}

action_method.do是后台具体某一接口的接口名。问号后面是请求所携带的参数。

前三个参数已经写死了,format指数据格式,本项目使用json格式。encryptType是报文加密格式(001在项目中是DES加密)。isEncrypt表示是否加密,1代表加密。

后面两个参数“head”和“data”是关键参数。其中head代表项目中公共的参数,从构造方法中可以看到,里面包含了手机硬件的各种信息以及用户的登录名。这样后台想要获取用户名,就可以每次直接从head里面取值了。

data代表不同业务逻辑自己需要的参数。比如说调用查询历史记录的接口,你需要传给后台“日期”参数;调用保存个性签名的接口,你需要上传编写的个性签名字符串作为参数。

data = new HashMap<String, String>();

data是一个HashMap,前一个key是后台指定的接口字段,后一个value就是字段对应的具体值。在构造Parameter对象的时候,我们可以自己十一画一个HashMap,传入key和value,然后通过Parameter的setData方法进行赋值。



NetworkCallBack是一个简单的抽象类,主要用于将OKHttp中的请求回调进行改造:

public abstract class NetworkCallBack {

    public abstract void onStart();

    /**
     * 网络请求或者文件上传下载成功的结果回调
     *
     * @param result 保存网络请求返回数据
     */
    public abstract void onSuccess(String result);


    /**
     * 网络请求或者文件上传下载失败的回调方法
     *
     * @param status 接口返回状态信息
     */
    public abstract void onFail(NetworkStatus status);


    /**
     * 上传或者下载文件的时候进度返回
     *
     * @param current  当前字节数
     * @param total    总字节数
     * @param progress 进度百分比
     */
    public void onProgress(long current, long total, int progress) {

    }

    public void onLoadPictureSuccess(byte[] bytes) {

    }
}

其中onStart(), onSuccess(), onFail()这三个方法是必须要重写的。



最后是最重要的类NetworkUtil,就是在这个类里面对OKHttp进行了简单的二次封装。

public class NetworkUtil {

    private final String TAG = getClass().getSimpleName() + "-->>";

    /**
     * 当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁
     * 零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:
     * 生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
     */
    private static final byte[] LOCK = new byte[0];
    private static NetworkUtil mInstance;
    private OkHttpClient mOkHttpClient;

    private final static int WHAT_SUCCESS = 1;
    private final static int WHAT_FAIL = 2;
    private final static int WHAT_PROGRESS = 3;

    public static NetworkUtil getInstance() {
        if (mInstance == null) {
            synchronized (LOCK) {
                if (mInstance == null) {
                    mInstance = new NetworkUtil();
                }
            }
        }
        return mInstance;
    }

    /**
     * 构造方法
     * 实现 mOkHttpClient
     */
    private NetworkUtil() {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();

        clientBuilder.cookieJar(new CookieJar() {
            private String JSESESSION_VALUE = null;
            private ArrayList<Cookie> cookies = new ArrayList<>();

            @Override
            public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {

                if(TextUtils.isEmpty(JSESESSION_VALUE)){
                    for(Cookie cookie:cookies){
//                        Log.d(TAG, url + "\nname = " + cookie.name() + "\nvalue = " + cookie.value());
                        if(cookie.name().equals("JSESSIONID")){
                            JSESESSION_VALUE = cookie.value();
                            this.cookies.clear();
                            this.cookies.add(cookie);
                            break;
                        }
                    }
                }

//                Log.d(TAG, "saveFromResponse:" + url.host());
                for(Cookie cookie:cookies){
//                    Log.d(TAG, url + "\nname = " + cookie.name() + "\nvalue = " + cookie.value());
                }
            }

            @Override
            public List<Cookie> loadForRequest(HttpUrl url) {
                if(cookies != null && cookies.size() > 0){
                    for(Cookie cookie:cookies){
//                        Log.d(TAG, url + "\nname = " + cookie.name() + "\nvalue = " + cookie.value());
                    }
                }
                return cookies;
            }
        });
        clientBuilder.readTimeout(NetworkConfig.NETWORK_READ_TIMEOUT, TimeUnit.SECONDS);
        clientBuilder.connectTimeout(NetworkConfig.NETWORK_CONNECT_TIMEOUT, TimeUnit.SECONDS);
        clientBuilder.writeTimeout(NetworkConfig.NETWORK_WRITE_TIMEOUT, TimeUnit.SECONDS);
        mOkHttpClient = clientBuilder.build();
    }


    /**
     * 按照接口名称请求数据
     * @param method
     * @param parameter
     * @param callBack
     */
    public void post(final String method, Parameter parameter, final NetworkCallBack callBack){
        postFullUrl(NetworkConfig.BASE_ACTION_URL + method, parameter, callBack);
    }



    /**
     * post方法提交表单信息
     * @param fullUrl 接口地址
     * @param parameter 请求参数
     * @param callBack 自定义的回调接口
     */
    public void postFullUrl(final String fullUrl, Parameter parameter, final NetworkCallBack callBack){

        final Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                switch (msg.what) {
                    case WHAT_SUCCESS:
                        String result = (String) msg.obj;
                        callBack.onSuccess(result);
                        break;
                    case WHAT_FAIL:
                        NetworkStatus status = (NetworkStatus) msg.obj;
                        callBack.onFail(status);
                        break;
                }
            }
        };


        FormBody.Builder builder = new FormBody.Builder();

        String format = parameter.format;
        String isKey = parameter.isEncrypt;
        String keyType = parameter.encryptType;
        String head = parameter.getHeadString();
        String data = parameter.getDataString();

        try { // 将传过来的参数字符串加密
            head = Des3.encode(head);
            data = Des3.encode(data);
        } catch (Exception e1) {
            throw new RuntimeException(e1);
        }

        builder.add("format", format);
        builder.add("isKey", isKey);
        builder.add("keyType", keyType);
        builder.add("head", head);
        builder.add("data", data);


        //请求体
        RequestBody body = builder.build();

        //构建请求对象
        Request request = new Request.Builder()
                .url(fullUrl)
                .post(body)
                .build();

        callBack.onStart();

        //发起请求
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_IO_EXCEPTION));
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                String result = response.body().string();

                if(TextUtils.isEmpty(result)){
                    handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR));
                    return;
                }

                try {
                    String decodeResult = Des3.decode(result);
                    Log.d(TAG, "response:" + fullUrl + "\n" + decodeResult);

                    Gson gson = new GsonBuilder().create();
                    BaseResponseEntity baseResponse = gson.fromJson(decodeResult, BaseResponseEntity.class);

                    switch (NetworkStatus.getNetworkStatusByCode(baseResponse.getCode())){
                        case STATUS_SUCCESS:
                            handler.sendMessage(handler.obtainMessage(WHAT_SUCCESS, decodeResult));
                            break;
                        case STATUS_FAIL_PARAMS_ERROR:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARAMS_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                        case STATUS_FAIL_SERVICE_ERROR:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                        case STATUS_FAIL_SESSION_OUT:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SESSION_OUT.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                        default:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setCode(baseResponse.getCode()).setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARSING_ERROR));
                }
            }
        });
    }

}

这是一个单例类。

在构造方法中进行了cookie的处理,已经http请求超时的设置。(当然都是用的okhttp库的方法)

接下来的post方法和postFullUrl方法其实是一样的,只不过post方法只需要传入接口名,再将请求的url补全了以后传给postFullUrl方法。

· postFullUrl
首先实例化了一个Handler,这个在后面接收response的时候会用到。

然后创建FormBody实例,将接口所需的五个参数加入;
再创建okhttp的请求体RequestBody;
再构建请求对象Request。

此时发送http请求的准备工作已经就绪。
我们在发送请求之前,调用NetworkCallBack的onStart()方法。
(NetworkCallBack的具体实现在业务代码中,方法的重写自然也在具体业务代码里面进行,我会在最后写个小demo。)

接下来正式发起请求。

        //发起请求
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_IO_EXCEPTION));
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                String result = response.body().string();

                if(TextUtils.isEmpty(result)){
                    handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR));
                    return;
                }

                try {
                    String decodeResult = Des3.decode(result);
                    Log.d(TAG, "response:" + fullUrl + "\n" + decodeResult);

                    Gson gson = new GsonBuilder().create();
                    BaseResponseEntity baseResponse = gson.fromJson(decodeResult, BaseResponseEntity.class);

                    switch (NetworkStatus.getNetworkStatusByCode(baseResponse.getCode())){
                        case STATUS_SUCCESS:
                            handler.sendMessage(handler.obtainMessage(WHAT_SUCCESS, decodeResult));
                            break;
                        case STATUS_FAIL_PARAMS_ERROR:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARAMS_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                        case STATUS_FAIL_SERVICE_ERROR:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                        case STATUS_FAIL_SESSION_OUT:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SESSION_OUT.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                        default:
                            handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setCode(baseResponse.getCode()).setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
                            break;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARSING_ERROR));
                }
            }
        });

在onFailure和onResponse两个回调中,根据不同的情况,发送不同的message给最前面的handler。

BaseResponseEntity是项目用的实体类,用来接收服务器返回的code和message。

public class BaseResponseEntity implements Serializable {

    private String code;

    private String message;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

在对code进行区分的时候,用到了之前的类NetworkStatus中的getNetworkStatusByCode()方法。



这样,一个简单的基于okhttp3的网络请求框架就封装好了,我们可以很方便的使用它,举个例子:

//从服务器获取信息的方法
private void getInfo() {
        Parameter parameter = new Parameter(this);
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("name", "zz");
        map.put("age", "19");
        parameter.setData(map);

        NetworkUtil.getInstance().post("getInfo.do", parameter, new NetworkCallBack() {
            @Override
            public void onStart() {
                showProgressDialog("正在获取info");
            }

            @Override
            public void onSuccess(String result) {
                dismissProgressDialog();
                Gson gson = new GsonBuilder().create();
                Info info = gson.fromJson(result, Info.class);
                //后续操作···
            }

            @Override
            public void onFail(NetworkStatus status) {
                dismissProgressDialog();
                processNetWorkFailed(status, true, false);
            }
        });
    }

processNetWorkFailed是一个用于处理请求失败的方法。

 /**
     * 处理请求失败的时候的逻辑
     * @param status
     * @param showToast
     * @param finish
     */
    protected void processNetWorkFailed(NetworkStatus status, boolean showToast, boolean finish) {
        switch (status) {
            case STATUS_FAIL_IO_EXCEPTION:
            case STATUS_FAIL_PARAMS_ERROR:
            case STATUS_FAIL_SERVICE_ERROR:
            case STATUS_FAIL_PARSING_ERROR:
                if (showToast) {
                    uiHandler.showShortToast(status.getMessage());
                }

                if (finish) {
                    finish();
                }
                break;
            case STATUS_FAIL_SESSION_OUT:
                if (!isFinishing()) {
                    uiHandler.showShortToast(status.getMessage());
                    AccountsPreference.setAutoLogin(false);
                    Intent intent = new Intent(BaseActivity.this, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(intent);
                    finish();
                }
                break;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值