OkHttp使用详解

一,简介

OkHttp 是一个高效的 HTTP 客户端,具有非常多的优势:

  1. 能够高效的执行 http,数据加载速度更快,更省流量
  2. 支持 GZIP 压缩,提升速度,节省流量
  3. 缓存响应数据,避免了重复的网络请求
  4. 使用简单,支持同步阻塞调用和带回调的异步调用

OkHttp 支持 Android2.3 以上,JDK1.7 以上。

官网地址:https://square.github.io/okhttp/
github地址:https://github.com/square/okhttp

二,基本用法

添加 OkHttp 依赖
compile ‘com.squareup.okhttp3:okhttp:(insert latest version)‘

1. Get 请求

使用 OkHttp 进行 Get 请求只需要四个步骤

  1. 新建 OkHttpClient对象
  2. 构造 Request 对象
  3. 将 Request 对象封装为 Call
  4. 通过 Call 来执行同步或异步请求
String url = "http://www.xxxx.com";
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .get()  //默认为GET请求,可以不写
                .build();
       final Call call = client.newCall(request);

1.1 Get 同步请求

通过 call.excute() 方法来提交同步请求,这种方式会阻塞线程,而为了避免 ANR 异常,Android3.0 之后已经不允许在主线程中访问网络了
所以 OkHttp 的同步 get 请求需要开启一个子线程:

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response response = call.execute();
                    Log.d(TAG, response.body().toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

1.2 Get 异步请求

通过 call.enqueue(Callback)方法来提交异步请求

call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "onFailure: " + e);
            }
 
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "OnResponse: " + response.body().toString());
            }
        });

2. Post 请求

创建 Post 请求的方式与 Get 方法类似,只是需要增加一个步骤,构造出一个请求参数对象RequestBody ,用于携带我们需要提交的数据。(下面均以 Post 的异步请求为例,Post 同步请求只需将 call.enqueue() 替换成 call.execute() 即可)

public Builder post(RequestBody body)

Request 的 post 方法所接收的参数是 RequestBody 对象,所以只要是 RequestBody 类及其子类都可以当做参数传入。

RequestBody是一个抽象类,常用的 RequestBody 实现类有这么几种:

2.1 FormBody

FormBody是RequestBody的实现类,用于表单方式的请求

OkHttpClient client = new OkHttpClient();
        //创建表单请求参数
        FormBody.Builder builder = new FormBody.Builder();
        builder.add("name", "zhangsan");
        builder.add("age", "18");
        FormBody formBody = builder.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) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

2.2 RequestBody.create(...)

RequestBody 是一个抽象类,我们不能直接使用它,但是可以通过调用它的静态create方法来获取一个RequestBody对象,该方法会创建并返回一个 RequestBody 的匿名内部类实例
查看一下 RequestBody 类,发现它有这样几个 create 方法。

其中前三个方法最终调用的都是第四个方法,所以我们可以具体看一下最后两个方法的具体实现

/** Returns a new request body that transmits {@code content}. */
  public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
      final int offset, final int byteCount) {
    if (content == null) throw new NullPointerException("content == null");
    Util.checkOffsetAndCount(content.length, offset, byteCount);
    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }
      @Override public long contentLength() {
        return byteCount;
      }
      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.write(content, offset, byteCount);
      }
    };
  }
  /** Returns a new request body that transmits the content of {@code file}. */
  public static RequestBody create(final @Nullable MediaType contentType, final File file) {
    if (file == null) throw new NullPointerException("content == null");
    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }
      @Override public long contentLength() {
        return file.length();
      }
      @Override public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
          source = Okio.source(file);
          sink.writeAll(source);
        } finally {
          Util.closeQuietly(source);
        }
      }
    };
  }

这里多解释一下
Content-Type(MediaType),即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件。常见的媒体格式类型有:

使用 create 方法可以用来用于上传 String 和 File 对象,具体实现如下:
上传JSON字符串:

OkHttpClient client = new OkHttpClient();
        //指定当前请求的 contentType 为 json 数据
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        String jsonStr = "{"name":"zhangsan","age":"20"}";
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(JSON, jsonStr))
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

上传文件:

OkHttpClient client = new OkHttpClient();
        File file = new File(filePath);
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

2.3 使用MultipartBody同时上传多种类型数据

多文件和键值对同时上传

OkHttpClient client = new OkHttpClient();
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("name", "zhangsan")
                .addFormDataPart("age", "20")
                .addFormDataPart("file", file.getName(),
                        RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();
        
        Request request = new Request.Builder()
                .url(url)
                .post(multipartBody)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });