Android android网络开发Andriod收藏

06-OkHttp的使用以及简单封装

2016-08-04  本文已影响2887人  小楠总

一、Android中网络请求的进化

Android开发中网络编程是必不可少的,不接入互联网的APP就没有盈利可言。废话不多说了,下面请看Android中网络请求的进化图:

HttpURLConnection,Apache HTTP Client,Volley到现在的OKHttp,可谓天外有天,人外有人。为什么OKHttp会这么火呢,相信下面的介绍会告诉你答案。

二、OKHttp的简介

首先,给出OKHttp的项目地址:https://github.com/square/okhttp

Android为我们提供了两种HTTP交互的方式: HttpURLConnection 和 Apache HTTP Client,虽然两者都支持HTTPS,流的上传和下载,配置超时,IPv6和连接池,已足够满足我们各种HTTP请求的需求。但更高效的使用HTTP可以让您的应用运行更快、更节省流量。而OkHttp库就是为此而生。 OkHttp是一个高效的HTTP库:
1.支持 SPDY ,共享同一个Socket来处理同一个服务器的所有请求
2.如果SPDY不可用,则通过连接池来减少请求延时
3.无缝的支持GZIP来减少数据流量

  1. 缓存响应数据来减少重复的网络请求

OkHttp会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。(并发) 使用 OkHttp 无需重写您程序中的网络代码。OkHttp实现了几乎和java.net.HttpURLConnection一样的API。如果您用了 Apache HttpClient,则OkHttp也提供了一个对应的okhttp-apache 模块。 从上面的简单介绍中可以知道,虽然在编程上面并不会简洁很多,但是OKHttp内部的一些功能能够帮助我们自动完成一些很复杂的操作,笔者个人认为最大的卖点就是大大节省用户的流量。

三、OKHttp的基本使用

1、在Gradle中引入CardView的依赖库。

compile 'com.squareup.okhttp:okhttp:2.7.2'

2、OKHttp在使用之前,首先要了解下面几个比较核心的类以及它的功能。

3、OKHttp的基本使用步骤(以POST方式从服务器取JSON数据为例)

    OkHttpClient client = new OkHttpClient();
    
    RequestBody body = new FormEncodingBuilder()
            .add("type", "1")
            .build();
    
    Request request = new Request.Builder()
            .url(Constants.URL_BANNER)
            .post(body)
            .build();
    
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Request request, IOException e) {
    
        }
    
        @Override
        public void onResponse(Response response) throws IOException {
            if (response.isSuccessful()) {
    
                android.os.Message msg = new Message();
                msg.what = 1;
                msg.obj = response.body().string();
                mHandler.sendMessage(msg);
    
            }
        }
    });

这里只是一个简单post请求,从服务器获取数据的介绍,至于get请求只不过是去掉RequestBody对象而已。至于如何向服务器提交数据,可以在熟悉上面的基本用法之后查阅官方的WIKI(GitHub的介绍文档)。另外楠妹妹也推荐下面这一篇文章,http://blog.csdn.net/lmj623565791/article/details/47911083

四、OKHttp的简单封装

回顾上面的代码,试想一下如果每次请求都写这么多重复代码,这样会严重降低开发效率,因此需要对OKHttp进行封装。对代码进行封装是我们最为面向对象程序员的基本素养,减少重复代码,降低维护难度以及成本。

GitHub上也有对OKHttp进行过封装,叫做OKHttpUtils。不过这里我们自己动手,一起来学习一下如何进行封装。具体的注意点有下面几点:

1、首先,OKHttp官方要求我们最好用单例模式去使用OKHttpClient类的,因此我们自定义一个OKHttpHelper类,并且使用单例模式。

2、对get以及post方法进行封装,主要的思想是把共同的代码抽取出来,例如代码中被抽取出来的request方法。

3、对外公开一些静态方法,包括get和post方法等。

4、Callback基类,对OKHttp的回调进行封装。这个类用里面有一个type,是方便回调中使用Gson对JSON进行解析的封装。使用Callback的时候只需要在泛型中传入类似Data 、List<Data>即可以方便地使用JSON。

5、由于原来的回调不在主线程,因此我们需要使用Handler来将回调放入主线程。

其余的可以参照代码,有详细注释。

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

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 这个类用来辅助OKHttp
 */
public class OkHttpHelper {

    /**
     * 采用单例模式使用OkHttpClient
     */
    private static OkHttpHelper mOkHttpHelperInstance;
    private static OkHttpClient mClientInstance;
    private Handler mHandler;
    private Gson mGson;

    /**
     * 单例模式,私有构造函数,构造函数里面进行一些初始化
     */
    private OkHttpHelper() {
        mClientInstance = new OkHttpClient();

        mClientInstance.setConnectTimeout(10, TimeUnit.SECONDS);
        mClientInstance.setReadTimeout(10, TimeUnit.SECONDS);
        mClientInstance.setWriteTimeout(30, TimeUnit.SECONDS);
        mGson = new Gson();

        mHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 获取实例
     *
     * @return
     */
    public static OkHttpHelper getinstance() {

        if (mOkHttpHelperInstance == null) {

            synchronized (OkHttpHelper.class) {
                if (mOkHttpHelperInstance == null) {
                    mOkHttpHelperInstance = new OkHttpHelper();
                }
            }
        }
        return mOkHttpHelperInstance;
    }

    /**
     * 封装一个request方法,不管post或者get方法中都会用到
     */
    public void request(final Request request, final BaseCallback callback) {

        //在请求之前所做的事,比如弹出对话框等
        callback.onRequestBefore();

        mClientInstance.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                //返回失败
                callbackFailure(request, callback, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                if (response.isSuccessful()) {
                    //返回成功回调
                    String resString = response.body().string();

                    if (callback.mType == String.class) {
                        //如果我们需要返回String类型
                        callbackSuccess(response, resString, callback);
                    } else {
                        //如果返回的是其他类型,则利用Gson去解析
                        try {
                            Object o = mGson.fromJson(resString, callback.mType);
                            callbackSuccess(response, o, callback);
                        } catch (JsonParseException e) {
                            e.printStackTrace();
                            callbackError(response, callback, e);
                        }
                    }

                } else {
                    //返回错误
                    callbackError(response, callback, null);
                }
            }
        });
    }

    /**
     * 在主线程中执行的回调
     *
     * @param response
     * @param resString
     * @param callback
     */
    private void callbackSuccess(final Response response, final Object o, final BaseCallback callback) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onSuccess(response, o);
            }
        });
    }

    /**
     * 在主线程中执行的回调
     * @param response
     * @param callback
     * @param e
     */
    private void callbackError(final Response response, final BaseCallback callback, final Exception e) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onError(response, response.code(), e);
            }
        });
    }

    /**
     * 在主线程中执行的回调
     * @param request
     * @param callback
     * @param e
     */
    private void callbackFailure(final Request request, final BaseCallback callback, final Exception e) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onFailure(request, e);
            }
        });
    }

    /**
     * 对外公开的get方法
     *
     * @param url
     * @param callback
     */
    public void get(String url, BaseCallback callback) {
        Request request = buildRequest(url, null, HttpMethodType.GET);
        request(request, callback);
    }

    /**
     * 对外公开的post方法
     *
     * @param url
     * @param params
     * @param callback
     */
    public void post(String url, Map<String, String> params, BaseCallback callback) {
        Request request = buildRequest(url, params, HttpMethodType.POST);
        request(request, callback);
    }

    /**
     * 构建请求对象
     *
     * @param url
     * @param params
     * @param type
     * @return
     */
    private Request buildRequest(String url, Map<String, String> params, HttpMethodType type) {
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (type == HttpMethodType.GET) {
            builder.get();
        } else if (type == HttpMethodType.POST) {
            builder.post(buildRequestBody(params));
        }
        return builder.build();
    }

    /**
     * 通过Map的键值对构建请求对象的body
     *
     * @param params
     * @return
     */
    private RequestBody buildRequestBody(Map<String, String> params) {

        FormEncodingBuilder builder = new FormEncodingBuilder();

        if (params != null) {
            for (Map.Entry<String, String> entity : params.entrySet()) {
                builder.add(entity.getKey(), entity.getValue());
            }
        }
        return builder.build();
    }

    /**
     * 这个枚举用于指明是哪一种提交方式
     */
    enum HttpMethodType {
        GET,
        POST
    }

}

回调的封装

package com.nan.cnshop.http;

import com.google.gson.internal.$Gson$Types;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 基本的回调
 */
public abstract class BaseCallback<T> {


    /**
     * type用于方便JSON的解析
     */
    public Type mType;

    /**
     * 把type转换成对应的类,这里不用看明白也行。
     *
     * @param subclass
     * @return
     */
    static Type getSuperclassTypeParameter(Class<?> subclass) {
        Type superclass = subclass.getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        ParameterizedType parameterized = (ParameterizedType) superclass;
        return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
    }

    /**
     * 构造的时候获得type的class
     */
    public BaseCallback() {
        mType = getSuperclassTypeParameter(getClass());
    }

    /**
     * 请求之前调用
     */
    public abstract void onRequestBefore();

    /**
     * 请求失败调用(网络问题)
     *
     * @param request
     * @param e
     */
    public abstract void onFailure(Request request, Exception e);

    /**
     * 请求成功而且没有错误的时候调用
     *
     * @param response
     * @param t
     */
    public abstract void onSuccess(Response response, T t);

    /**
     * 请求成功但是有错误的时候调用,例如Gson解析错误等
     *
     * @param response
     * @param errorCode
     * @param e
     */
    public abstract void onError(Response response, int errorCode, Exception e);

}

五、OKHttp封装之后的使用

如下面的代码所示。首先得到OkHttpHelper的单例,然后调用get方法就可以了。由于继承了Gson,因此需要在BaseCallback的泛型中传入JSON对应的数据类型,笔者这里是List<Banner>。最后在onSuccess方法中做我们想要做的事情就可以了。

mHttpHelper=OkHttpHelper.getinstance();
mHttpHelper.get(Constants.URL_BANNER, new BaseCallback<List<Banner>>() {

    @Override
    public void onRequestBefore() {

    }

    @Override
    public void onFailure(Request request, Exception e) {

    }

    @Override
    public void onSuccess(Response response, List<Banner> banners) {
        initBanners(banners);
    }

    @Override
    public void onError(Response response, int errorCode, Exception e) {

    }
});

是不是觉得封装之后OKHttp的使用变得很简单呢,这就是封装的强大之处,好了今天的笔记到此为止,明天见O(∩_∩)O~~。

PS:这里只介绍了OKHttp的get和post使用介绍,其余的使用例如文件下载上传以及对应的代码封装请自己去完成(≧▽≦)/啦啦啦。

五、附件

BaseCallback.java

package com.nan.cnshop.http;

import com.google.gson.internal.$Gson$Types;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 基本的回调
 */
public abstract class BaseCallback<T> {


    /**
     * type用于方便JSON的解析
     */
    public Type mType;

    /**
     * 把type转换成对应的类,这里不用看明白也行。
     *
     * @param subclass
     * @return
     */
    static Type getSuperclassTypeParameter(Class<?> subclass) {
        Type superclass = subclass.getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        ParameterizedType parameterized = (ParameterizedType) superclass;
        return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
    }

    /**
     * 构造的时候获得type的class
     */
    public BaseCallback() {
        mType = getSuperclassTypeParameter(getClass());
    }

    /**
     * 请求之前调用
     */
    public abstract void onRequestBefore();

    /**
     * 请求失败调用(网络问题)
     *
     * @param request
     * @param e
     */
    public abstract void onFailure(Request request, Exception e);

    /**
     * 请求成功而且没有错误的时候调用
     *
     * @param response
     * @param t
     */
    public abstract void onSuccess(Response response, T t);

    /**
     * 请求成功但是有错误的时候调用,例如Gson解析错误等
     *
     * @param response
     * @param errorCode
     * @param e
     */
    public abstract void onError(Response response, int errorCode, Exception e);

}

OkHttpHelper.java

package com.nan.cnshop.http;

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

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 这个类用来辅助OKHttp
 */
public class OkHttpHelper {

    /**
     * 采用单例模式使用OkHttpClient
     */
    private static OkHttpHelper mOkHttpHelperInstance;
    private static OkHttpClient mClientInstance;
    private Handler mHandler;
    private Gson mGson;

    /**
     * 单例模式,私有构造函数,构造函数里面进行一些初始化
     */
    private OkHttpHelper() {
        mClientInstance = new OkHttpClient();

        mClientInstance.setConnectTimeout(10, TimeUnit.SECONDS);
        mClientInstance.setReadTimeout(10, TimeUnit.SECONDS);
        mClientInstance.setWriteTimeout(30, TimeUnit.SECONDS);
        mGson = new Gson();

        mHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 获取实例
     *
     * @return
     */
    public static OkHttpHelper getinstance() {

        if (mOkHttpHelperInstance == null) {

            synchronized (OkHttpHelper.class) {
                if (mOkHttpHelperInstance == null) {
                    mOkHttpHelperInstance = new OkHttpHelper();
                }
            }
        }
        return mOkHttpHelperInstance;
    }

    /**
     * 封装一个request方法,不管post或者get方法中都会用到
     */
    public void request(final Request request, final BaseCallback callback) {

        //在请求之前所做的事,比如弹出对话框等
        callback.onRequestBefore();

        mClientInstance.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                //返回失败
                callbackFailure(request, callback, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                if (response.isSuccessful()) {
                    //返回成功回调
                    String resString = response.body().string();

                    if (callback.mType == String.class) {
                        //如果我们需要返回String类型
                        callbackSuccess(response, resString, callback);
                    } else {
                        //如果返回的是其他类型,则利用Gson去解析
                        try {
                            Object o = mGson.fromJson(resString, callback.mType);
                            callbackSuccess(response, o, callback);
                        } catch (JsonParseException e) {
                            e.printStackTrace();
                            callbackError(response, callback, e);
                        }
                    }

                } else {
                    //返回错误
                    callbackError(response, callback, null);
                }
            }
        });
    }

    /**
     * 在主线程中执行的回调
     *
     * @param response
     * @param resString
     * @param callback
     */
    private void callbackSuccess(final Response response, final Object o, final BaseCallback callback) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onSuccess(response, o);
            }
        });
    }

    /**
     * 在主线程中执行的回调
     * @param response
     * @param callback
     * @param e
     */
    private void callbackError(final Response response, final BaseCallback callback, final Exception e) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onError(response, response.code(), e);
            }
        });
    }

    /**
     * 在主线程中执行的回调
     * @param request
     * @param callback
     * @param e
     */
    private void callbackFailure(final Request request, final BaseCallback callback, final Exception e) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onFailure(request, e);
            }
        });
    }

    /**
     * 对外公开的get方法
     *
     * @param url
     * @param callback
     */
    public void get(String url, BaseCallback callback) {
        Request request = buildRequest(url, null, HttpMethodType.GET);
        request(request, callback);
    }

    /**
     * 对外公开的post方法
     *
     * @param url
     * @param params
     * @param callback
     */
    public void post(String url, Map<String, String> params, BaseCallback callback) {
        Request request = buildRequest(url, params, HttpMethodType.POST);
        request(request, callback);
    }

    /**
     * 构建请求对象
     *
     * @param url
     * @param params
     * @param type
     * @return
     */
    private Request buildRequest(String url, Map<String, String> params, HttpMethodType type) {
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (type == HttpMethodType.GET) {
            builder.get();
        } else if (type == HttpMethodType.POST) {
            builder.post(buildRequestBody(params));
        }
        return builder.build();
    }

    /**
     * 通过Map的键值对构建请求对象的body
     *
     * @param params
     * @return
     */
    private RequestBody buildRequestBody(Map<String, String> params) {

        FormEncodingBuilder builder = new FormEncodingBuilder();

        if (params != null) {
            for (Map.Entry<String, String> entity : params.entrySet()) {
                builder.add(entity.getKey(), entity.getValue());
            }
        }
        return builder.build();
    }

    /**
     * 这个枚举用于指明是哪一种提交方式
     */
    enum HttpMethodType {
        GET,
        POST
    }

}

如果觉得我的文字对你有所帮助的话,欢迎关注我的公众号:

公众号:Android开发进阶

我的群欢迎大家进来探讨各种技术与非技术的话题,有兴趣的朋友们加我私人微信huannan88,我拉你进群交(♂)流(♀)

上一篇下一篇

猜你喜欢

热点阅读