Android OkHttp(一)初识

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxw136511485/article/details/52759378

    目前在Android开发中,开源的网络请求框架有Android-async-http  ,Volley,Okhttp等,这几种框架都是市面上主流的,每种都有各自的优点。今天主要来看看Okhttp。之前的项目中没有使用过Okhttp,所以这篇文章也是自己对Okhttp的初步认识,如有错误,欢迎指出!

一、概述。

OkHttp官网地址:http://square.github.io/okhttp/

OkHttp GitHub地址:https://github.com/square/okhttp

首先看看OkHttp官网的介绍,

An HTTP & HTTP/2 client for Android and Java applications.

OkHttp是一个为Android和Java应用程序提供HTTP 或 HTTP/2的客户端。

HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load 
faster and saves bandwidth.
OkHttp is an HTTP client that’s efficient by default:
    HTTP/2 support allows all requests to the same host to share a socket.
    Connection pooling reduces request latency (if HTTP/2 isn’t available).
    Transparent GZIP shrinks download sizes.
    Response caching avoids the network completely for repeat requests.
OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service
has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 
and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), 
and falls back to TLS 1.0 if the handshake fails.
Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous 
blocking calls and async calls with callbacks.
OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.
HTTP是现代应用网络的方式。它描述了如何交换数据及媒体。有效的使用HTTP,可以使得你更快的加载数据,节省带宽。

OkHttp默认是一个有效的HTTP客户端:

    HTTP/2支持允许所有的请求相同的主机共享同一个套接字。

    连接池可以减少请求延迟(HTTP/2不可使用)。

    下载文件透明的GZIP压缩。

   响应缓存可以避免完全重复的网络请求。

当网络不好时,OkHttp是很坚强的:它会默认从常见的连接问题中恢复过来。如果你的服务有多个IP地址,如果第一个连接失败,那么OkHttp将尝试连接备用的地址。这是必须的对于IPv4+IPv6和冗余数据中心托管的服务。OkHttp发起与新的TLS特性(SNI, ALPN)连接,如何握手失败,将退回到TLS 1.0。

使用OkHttp很容易。 其请求/响应API设计具有流畅的构建器和不变性。 它支持同步阻塞呼叫和带回调的异步调用。
OkHttp支持Android 2.3及更高版本。 对于Java,最低要求是1.7。

2.使用配置。

(1). 下载jar包,

点击后面的下载连接,可以下载最新的jar, OkHttp最新jar包下载地址。

(2). Maven配置,

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.4.1</version>
</dependency>

(3). Gradle配置,

compile 'com.squareup.okhttp3:okhttp:3.4.1'

二、使用例子。

1. GET请求。

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}
(1).OkHttpClient:新建一个OkHttpClient实例,用于处理请求。

(2).Request:Request是OkHttp中访问的请求类。

(3).Builder:是辅助类,构建请求参数,如url,请求方式,请求参数,header等。

(4).Call:生成一个具体请求实例,相当于将请求封装成了任务;两种方式:
    ①、call.execute(),非异步方式,会阻塞线程,等待返回结果。这种方式不会开启新的线程,要在android中使用这个需要自己开启一个新线程,然后在线程中调用这个东西。  

   ②、call.enqueue(Callback),异步方式。call.enqueue会开启一个新的线程,在新线程中调用网络请求。记得不要再回调方法中直接更新UI。可以通过Handler来与 android UI交互或者runOnUiThread()等。推荐使用这个异步方式。不需要自己新建线程,维护线程等。

(5).Response:响应结果。比如我们希望获得返回的字符串,可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。

2. POST请求。

POST请求跟Get基本相同,主要是两者的Request类的构造不一样,POST请求需要增加RequestBody来存储请求的参数信息。

public static final MediaType JSON
    = MediaType.parse("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  Response response = client.newCall(request).execute();
  return response.body().string();
}
上述例子中Request中携带了RequestBody(请求参数)。

当然请求参数也可以传入键值对,

  OkHttpClient client = new OkHttpClient();
  
  String post(String url) throws IOException {
  RequestBody formBody = new FormBody.Builder()
            .add("platform", "android")
            .add("name", "bug")
             .build();
  
  Request request = new Request.Builder()
      .url(url)
      .post(formBody)
      .build();
  Response response = client.newCall(request).execute();
  return response.body().string();
  }

3. 文件下载。

下载文件,可以使用POST或者GET。下面例子是使用POST请求,

  public void download(String url) throws IOException {
   OkHttpClient client = new OkHttpClient();
  RequestBody formBody = new FormBody.Builder()
            .add("version", "123")
             .build();
  
  Request request = new Request.Builder()
      .url(url)
      .post(formBody)
      .build();
   client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onCompleted(e, null);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream is = null;
                byte[] buf = new byte[1024];
                int len = 0;
                FileOutputStream fos = null;
                try {
                    is = response.body().byteStream();
                    File file = new File(destFileDir, StringUtils.hashKeyForDisk(url));
                    fos = new FileOutputStream(file);
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    callback.onCompleted(null, file.getAbsolutePath());
                } catch (IOException e) {
                    callback.onCompleted(e, null);
                } finally {
                    try {
                        if (is != null) is.close();
                    } catch (IOException e) {
                    }
                    try {
                        if (fos != null) fos.close();
                    } catch (IOException e) {
                    }
                }
            }
        });
  }
调用接口返回成功后,从响应中以流的方式写入到指定目录,最后返回目录!

4.文件上传。

OkHttp中的文件上传和web网页中的上传文件原理、方式基本一致。有关web网页中的上传文件,可以查看鸿洋的这篇文章, 从原理角度解析Android (Java) http 文件上传。上传文件如果需要传递参数,

	    if (params != null)  
        for (String key : params.keySet()) {  
            sb.append("--" + BOUNDARY + "\r\n");  
            sb.append("Content-Disposition: form-data; name=\"" + key  
                    + "\"" + "\r\n");  
            sb.append("\r\n");  
            sb.append(params.get(key) + "\r\n");  
        }else{
		ab.append("\r\n");
		}  
或者直接使用OkHttp提供的方法,

MultipartBody.Builder.addFormDataPart(key,value);
上传文件的代码是,

if (file != null) {
            RequestBody fileBody = RequestBody.create(MediaType.parse(CONTENTTYPEFOR), file);
            String fileName = file.getName();
            //根据文件名设置contentType
            builder.addPart(Headers.of("Content-Disposition",
                    "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\""),
                    fileBody);
        }
完整的代码见封装类。

如果我们不封装请求,那么代码比较杂乱,并且会有很多重复代码。

三、封装

/**
 * OkHttpClient 请求封装类
 */
public class OkHttpUtils {


    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    public static final String CONTENTTYPEFOR = "application/octet-stream";//二进制数据传输
    private volatile static OkHttpUtils mOkHttpUtils;

    public static OkHttpClient mOkHttpClient;
  

    public OkHttpUtils() {
        
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(300, TimeUnit.SECONDS).writeTimeout(300, TimeUnit.SECONDS).build();
    }

    public static OkHttpUtils getInstance() {
        if (mOkHttpUtils == null) {
            synchronized (OkHttpUtils.class) {
                if (mOkHttpUtils == null) {
                    mOkHttpUtils = new OkHttpUtils();
                }
            }
        }
        return mOkHttpUtils;
    }

    /**
     * get同步请求方法
     *
     * @param url
     * @param params
     * @return
     */
    public Response get(String url, Map<String, String> params) {
        Request request = buildRequest(buildRequestGetUrl(url, params), null, HttpMethodType.GET);
        return request(request);
    }

    /**
     * post同步请求方法
     *
     * @param url
     * @param params
     * @return
     */
    public Response post(String url, Map<String, String> params) {
        Request request = buildRequest(url, params, HttpMethodType.POST);
        return request(request);
    }

    /**
     * get异步请求方法
     *
     * @param url
     * @param callback
     */
    public static <T> void getAsyn(String url, Map<String, String> params, final TypeToken<T> typeToken, final BaseResponseCallback<T> callback) {
        Request request = buildRequest(buildRequestGetUrl(url, params), null, HttpMethodType.GET);
        request(request, typeToken, callback);
    }

    /**
     * post异步请求方法
     *
     * @param url
     * @param params
     * @param callback
     */
    public static <T> void postAsyn(String url, Map<String, String> params, final TypeToken<T> typeToken, final BaseResponseCallback<T> callback) {
        Request request = buildRequest(url, params, HttpMethodType.POST);
        request(request, typeToken, callback);
    }

    /**
     * 文件下载异步
     *
     * @param url         下载地址
     * @param params      参数
     * @param destFileDir 本地文件存储目录
     * @param callback    回调
     */
    public static void downloadFileAsyn(final String url, Map<String, String> params, final String destFileDir, final BaseResponseCallback callback) {
        Request request = buildRequest(buildRequestGetUrl(url, params), null, HttpMethodType.GET);
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onCompleted(e, null);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream is = null;
                byte[] buf = new byte[1024];
                int len = 0;
                FileOutputStream fos = null;
                try {
                    is = response.body().byteStream();
                    File file = new File(destFileDir);
                    fos = new FileOutputStream(file);
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    callback.onCompleted(null, file.getAbsolutePath());
                } catch (IOException e) {
                    callback.onCompleted(e, null);
                } finally {
                    try {
                        if (is != null) is.close();
                    } catch (IOException e) {
                    }
                    try {
                        if (fos != null) fos.close();
                    } catch (IOException e) {
                    }
                }
            }
        });
    }


    /**
     * 异步文件上传
     *
     * @param url
     * @param params
     * @param file
     * @param fileName
     */
    public static void postAsyn(String url, Map<String, String> params, File file, String fileName, final BaseResponseCallback callback) {
        Request request = buildFromRequestBody(url, params, file, fileName);
        request(request, callback);
    }


    /**
     * 构建请求对象
     *
     * @param url
     * @param params
     * @param type
     * @return
     */
    private static 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 static RequestBody buildRequestBody(Map<String, String> params) {
        FormBody.Builder builder = new FormBody.Builder();
        if (params != null && params.size() != 0) {
            for (Map.Entry<String, String> entity : params.entrySet()) {
                builder.add(entity.getKey(), entity.getValue());
            }
        }
        return builder.build();
    }

    //构造上传请求,类似web表单
    private static Request buildFromRequestBody(String url, Map<String, String> params, File file, String name) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        //设置类型
        builder.setType(MultipartBody.FORM);
        if (params != null && params.size() != 0) {
            for (Map.Entry<String, String> entity : params.entrySet()) {
                builder.addFormDataPart(entity.getKey(), entity.getValue());
            }
        }
        if (file != null) {
            RequestBody fileBody = RequestBody.create(MediaType.parse(CONTENTTYPEFOR), file);
            String fileName = file.getName();
            //根据文件名设置contentType
            builder.addPart(Headers.of("Content-Disposition",
                    "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\""),
                    fileBody);
        }
        RequestBody requestBody = builder.build();
        return new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
    }

    /**
     * 为HttpGet请求拼接多个参数
     *
     * @param url
     * @param params
     * @return
     */
    public static String buildRequestGetUrl(String url, Map<String, String> params) {
        StringBuffer stringBuilder = new StringBuffer();
        stringBuilder.append(url).append("?");
        try {
            if (params != null && params.size() != 0) {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    //转换成UTF-8
                    stringBuilder.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "utf-8"));
                    stringBuilder.append("&");
                }
                //删除最后一个字符&
                stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }

    public static Response request(final Request request) {
        Response response = null;
        try {
            response = mOkHttpClient.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }

    /**
     * 封装一个request方法,不管post或者get方法中都会用到
     */
    public static <T> void request(final Request request, final TypeToken<T> typeToken, final BaseResponseCallback<T> callback) {
        mOkHttpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                //返回失败
                callback.onCompleted(e, null);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    try {
                      //todo 解析json
                    } catch (JsonParseException exception) {
                        //json解析失败
                    }
                } else {
                    //返回失败
                }
            }
        });
    }

    /**
     * @param request
     * @param callback
     */
    public static  void request(final Request request, final BaseResponseCallback callback) {
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onCompleted(e, null);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    callback.onCompleted(null, responseBody);
                }
            }
        });
    }

    enum HttpMethodType {
        GET,
        POST
    }

}

至此,OkHttp的简单使用就结束!

PS:OkHttpUtils 下载链接

如果你还想对OkHttp有更多的了解,可以详看这篇文章,Android OkHttp(二)实战

没有更多推荐了,返回首页