1. OkHttp网络请求框架

OkHttp框架中几个比较重要的对象: OkHttpClient,Request,Call,Response。

1.1. Example

// step1: 创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(2, TimeUnit.SECONDS)
        .build();
// step2: 创建Request,该对象存储了该次请求所需要传递到服务器的信息,以及http请求的一些设置。
Request request = new Request.Builder()
        .url("http://10.0.2.2:3000/users/1")
        .addHeader("Authorization", "uid=10010")
        .method("GET", null)
        .build();
// step3: 使用client根据request创建Call对象
Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.e(TAG, "onFailure: " + e);
    }

    @Override
    public void onResponse(Call call, okhttp3.Response response) {
          // step4: 获取网络请求结果
        boolean success = response.isSuccessful();
        Log.e(TAG, "onResponse: " + success);
        String responseString = response.body().string();
        Log.e(TAG, "onResponse: " + responseString);
    }
});

通常一个应用中,OkHttpClient 实例是使用单例来存储,整个应用使用同一个 client 实例。

1.2. OkHttpClient

OkHttpClient表示的全局的网络请求配置,通过client创建的每一次请求都使用相同的配置。配置项可以通过 OkHttpClient.Builder 来快速创建配置。

File sdcache = getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .writeTimeout(20, TimeUnit.SECONDS)
    .readTimeout(20, TimeUnit.SECONDS)
    .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));

通过上面的三个 xxxTimeout 方法可以设置相应的超时时间,以及缓存文件存放位置。

除此之外,还有以下常用方法:

  • retryOnConnectionFailure : 设置失败重连;
  • addNetworkInterceptor : 添加拦截器,可以在拦截器中添加进行debug输入,添加请求头等;
  • addInterceptor: 添加拦截器,和addNetworkInterceptor相比,使用此方法添加的拦截器拦截到的整个网络的生命时间更长;

1.3. Request

Request表示一次请求的详细参数,比如请求方式,请求参数,Header等信息。使用Request.Builder可以快速的创建一个请求配置Request对象。

method

使用 Request.Builder 指定请求方式,有两种方式,一是使用 Request.Builder 的 method 方法,二是使用请求方式对应的方法名。

builder.method("GET", null); // 第二个参数为 RequestBody,可携带需要传给服务器的数据
// or
builder.get();

其它请求方式 POSTDELETEHEADPUTPATCH 等都有相对应的方法;

注意:GET 请求和 HEAD 请求,如果使用 method 来指定,则第二个参数必须为 null

addHeader

使用 addHeader 方法可以为请求添加请求头信息,也可以使用 header 或者 headers 方法指定请求头。

builder.addHeader("Authorization", "uid=10010");
// or
builder.header("Authorization", "uid=10010");

RequestBody

在使用 POSTDELETE 等请求时,需要使用 RequestBody 来传递参数到后台服务器,RequestBody 是一个抽象类,可以使用 FormBody来快速创建一个参数信息表单。

RequestBody body = new FormBody.Builder()
    .addEncoded("username", "ccf")
    .build();

这样创建的一个 FormBody 表单对象,在请求的时候就会被传递到服务器。

public final class FormBody extends RequestBody {
  private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");

  private final List<String> encodedNames;
  private final List<String> encodedValues;

  FormBody(List<String> encodedNames, List<String> encodedValues) {
    this.encodedNames = Util.immutableList(encodedNames);
    this.encodedValues = Util.immutableList(encodedValues);
  }

  public static final class Builder {
    private final List<String> names = new ArrayList<>();
    private final List<String> values = new ArrayList<>();
    private final Charset charset;

    public Builder() {
      this(null);
    }

    public Builder addEncoded(String name, String value) {
      names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, true, false, true, true, charset));
      values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, true, false, true, true, charset));
      return this;
    }
  }
}

除了FormBody之外,RequestBody还提供了几个重载的create方法,用于快速创建RequestBody;

  /**
   * Returns a new request body that transmits {@code content}.
   * and lacks a charset, this will use UTF-8.
   */
  public static RequestBody create(@Nullable MediaType contentType, String content) {
    Charset charset = Util.UTF_8;
    if (contentType != null) {
      charset = contentType.charset();
      if (charset == null) {
        charset = Util.UTF_8;
        contentType = MediaType.parse(contentType + "; charset=utf-8");
      }
    }
    byte[] bytes = content.getBytes(charset);
    return create(contentType, bytes);
  }

  /** Returns a new request body that transmits {@code content}. */
  public static RequestBody create(
      final @Nullable MediaType contentType, final ByteString content) {
    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }
      @Override public long contentLength() throws IOException {
        return content.size();
      }
      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.write(content);
      }
    };
  }

  /** Returns a new request body that transmits {@code content}. */
  public static RequestBody create(@Nullable MediaType contentType, byte[] content) {
    return create(contentType, content, 0, content.length);
  }

  /** Returns a new request body that transmits {@code content}. */
  public static RequestBody create(@Nullable MediaType contentType, byte[] content,
      final int offset, final int byteCount) {
  }

  /** Returns a new request body that transmits the content of {@code file}. */
  public static RequestBody create(final @Nullable MediaType contentType, final File file) {
    return new RequestBody() {
      @Override public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
          source = Okio.source(file);
          sink.writeAll(source);
        } finally {
          Util.closeQuietly(source);
        }
      }
    };
  }
File-Upload

文件上传也是一个POST请求,不过此时需要使用RequestBody,而不能直接使用FormBody,比如上传一个txt文件。

File file = ...;
RequestBody.Builder builder = new RequestBody.Builder();
// ...
builder.post(RequestBody.create(MediaType.parse("text/plain"), file));

相对应的,如果上传其它文件,则把MediaType更改成对应的type即可。

File-Download

文件下载和普通的 GET 请求在使用上没有区别,只是在 Callback#onResponse方法处理上,需要做一些调整。

@Override
public void onResponse(Call call, Response response) {
    InputStream inputStream = response.body().byteStream();
    // TODO 保存文件
    saveFile(inputStream);
}
MultipartBody

有时在上传文件的同时还需要传其他参数,此时可以使用 MultipartBody 上传文件和参数。

File file = ...;
RequestBody requestBody = new MultipartBody.Builder()
    .setType(MultipartBody.FORM)
    .addFormDataPart("title", "Awesome picture")
    .addFormDataPart("image", "awesome_picture.jpg",
            RequestBody.create(MediaType.parse("image/*"), file))
    .build();

Request request = new Request.Builder()
    .header("Authorization", "Client-ID " + "...")
    .url(...)
    .post(requestBody)
    .build();

1.4. Call

Call对象用于发起请求,发起请求有两种方式:

  • enqueue(Callback) 发起异步请求,使用Worker线程
  • execute 发起同步请求,使用当前代码所在的线程

当一个请求发起之后,可以使用 cancel()方法取消该请求。

使用client.newCall(request);创建一个Call对象。

1.5. Response

网络请求完成后,服务器的响应结果即Response。它包含了请求响应的状态码,响应头等信息。

public final class Response implements Closeable {
  final Request request;
  final Protocol protocol;
  final int code;
  final String message;
  final @Nullable Handshake handshake;
  final Headers headers;
  final @Nullable ResponseBody body;
  final @Nullable Response networkResponse;
  final @Nullable Response cacheResponse;
  final @Nullable Response priorResponse;
  final long sentRequestAtMillis;
  final long receivedResponseAtMillis;

}

results matching ""

    No results matching ""