Okhttp详解(网络)

超时设置:在onFailure中设置

 if (e.getClass().equals(SocketTimeoutException.class)) {
                callback.onFailure(new Exception("超时"), Constant.ERROR_NETWORK_TIMEOUT);

                return;
            }

强调内容
Call: 对象表示一个已经准备好可以执行的请求,用这个对象可以查询请求的执行状态,或者取消当前请求
注意: response.body()必须关闭,否则会造成资源泄露。

注意:
strting()的使用

响应主体上的string()方法对于小文档是方便和高效的。 但是如果响应体很大(大于1 MiB),
请避免使用string(),因为它会将整个文档加载到内存中。 在这种情况下,更喜欢将主体作为流处理。
response.body().string()仅可调用一次,否则会报错,---Caused by: java.lang.IllegalStateException: closed

(1)<同步的get请求>

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    Headers responseHeaders = response.headers();
    for (int i = 0; i < responseHeaders.size(); i++) {
      System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
    }

    System.out.println(response.body().string());
  }

(2)<异步的get请求>

在一个工作线程中下载文件,当响应可读时回调Callback接口。读取响应时会阻塞当前线程。OkHttp现阶段不提供异步api来接收响应体。

 private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

        Headers responseHeaders = response.headers();
        for (int i = 0, size = responseHeaders.size(); i < size; i++) {
          System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }

        System.out.println(response.body().string());
      }
    });
  }

(3)<异步的post请求>

   private void postAsynHttp() {
        OkHttpClient mOkHttpClient=new OkHttpClient();
        // 请求body
        RequestBody formBody = new FormBody.Builder()
                .add("size", "10")
                .build();
        Request request = new Request.Builder()
                .url("http://api.1-blog.com/biz/bizserver/article/list.do")
                .post(formBody)
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
             ResponseBody body = response.body();
             if (response.isSuccessful()) {
                if (body != null) {
                     String str = response.body().string();
                     Log.i("wangshu", str);
                       runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                        }
                    });
                    body.close();
               }

            }

        });
    }

第四:post方式提交String

使用HTTP POST提交请求到服务。这个例子提交了一个markdown文档到web服务,以HTML方式渲染markdown。因为整个请求体都在内存中,
因此避免使用此api提交大文档(大于1MB)。

 public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  } 

第五:post方式提交流

以流的方式POST提交请求体。请求体的内容由流写入产生。这个例子是流直接写入Okio的BufferedSink。你的程序可能会使用OutputStream,你可以使用BufferedSink.outputStream()来获取。

  public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    RequestBody requestBody = new RequestBody() {
      // 请求的类型
      @Override 
      public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }

      // 写出 请求的内容
      @Override 
      public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("Numbers\n");
        sink.writeUtf8("-------\n");
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
        }
      }

      // 通过运算写入的数据
      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + " × " + i;
        }
        return Integer.toString(n);
      }
    };

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

第六:异步post文件的下载

public void downAsynFile() {
    String url = "https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg";
    Request request = new Request.Builder().url(url).build();
    mOkHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.d("zhang", "onFailure: ");
        }

        @Override
        public void onResponse(Call call, Response response) {
            InputStream inputStream = response.body().byteStream();
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(new File("/sdcard/at/wangshu.jpg"));
                //=============
                final long total = response.body().contentLength();

                long sum = 0;

                //==============

                byte[] buffer = new byte[2048];
                int len = 0;
                while ((len = inputStream.read(buffer)) != -1) {
                    sum += len;
                    fileOutputStream.write(buffer, 0, len);
                    final long finalSum = sum;

                    // 进度的显示
                    Log.i("progress", "progress ==" + finalSum * 100.0f / total);
                }
                fileOutputStream.flush();
            } catch (IOException e) {
                Log.i("wangshu", "IOException");
                e.printStackTrace();
            }

            Log.d("wangshu", "文件下载成功");
        }
    });
}

第七:异步上传文件

MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");//mdiatype 这个需要和服务端保持一致

    File file = new File("/sdcard/at/wangshu.jpg");
    Request request = new Request.Builder()
            .url("https://api.github.com/markdown/raw")
            .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
            .build();

    mOkHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.d("wangshu","onFailure");
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            Log.d("wangshu",response.body().string());

            int code = response.code();
            Log.d("wangshu", "onSuccess==" + code);
        }

第八:post方式提交分块请求或者上传Multipart文件

MultipartBody.Builder可以构建与HTML文件上传表单兼容的复杂请求体。 多部分请求主体的每个部分本身都是请求主体,
并且可以定义其自己的标头。 如果存在,这些头部应描述部件主体,例如其Content-Disposition。
如果Content-Length和Content-Type标头可用,则会自动添加。
———————————————————————————-
addPart 就是发送头部的具体内容了,其中 addFormDataPart 封装了部分内容,本质上 和道理是一样的,建议使用addFormDataPart

  .addPart( 
  Headers.of(“Content-Disposition”, “form-data; name=\”token\”“), 
  RequestBody.create(null, uploadToken))

  如下面的源码介绍:
  public Builder addFormDataPart(String name, String filename, RequestBody body) {
  return addPart(Part.createFormData(name, filename, body));
  }

  public static Part createFormData(String name, String filename, RequestBody body) {
  if (name == null) {
    throw new NullPointerException("name == null");
  }
  StringBuilder disposition = new StringBuilder("form-data; name=");
  appendQuotedString(disposition, name);

  if (filename != null) {
    disposition.append("; filename=");
    appendQuotedString(disposition, filename);
  }

  return create(Headers.of("Content-Disposition", disposition.toString()), body);
}

---------------------------------------------------------------------------------------

例子:

  private static final String IMGUR_CLIENT_ID = "...";
  private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("title", "Square Logo")
        .addFormDataPart("image", "logo-square.png",
            RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
        .build();

    Request request = new Request.Builder()
        .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
        .url("https://api.imgur.com/3/image")
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

第九:使用Gson来解析JSON响应

Gson是一个在JSON和Java对象之间转换非常方便的api。这里我们用Gson来解析Github API的JSON响应
注意:ResponseBody.charStream()使用响应头Content-Type指定的字符集来解析响应体。默认是UTF-8。

  private final OkHttpClient client = new OkHttpClient();
  private final Gson gson = new Gson();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://api.github.com/gists/c2a7c39532239ff261be")
        .build();
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    // 得到封装的类属性
    Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
    for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
      System.out.println(entry.getKey());
      System.out.println(entry.getValue().content);
    }
  }

  static class Gist {
    Map<String, GistFile> files;
  }

  static class GistFile {
    String content;
  }

第十:设置超时时间和缓存

和OkHttp2.x有区别的是不能通过OkHttpClient直接设置超时时间和缓存了,而是通过OkHttpClient.Builder来设置,通过builder配置好OkHttpClient后用builder.build()来返回OkHttpClient,所以我们通常不会调用new OkHttpClient()来得到OkHttpClient,而是通过builder.build():

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

第十一:call配置(其实就是在请求数据的时候配置参数)

所有HTTP客户端配置都存在于OkHttpClient中,包括代理设置,超时和高速缓存。 当您需要更改单个调用的配置时,
请调用OkHttpClient.newBuilder()。 这将返回与原始客户端共享相同连接池,调度程序和配置的构建器。
在下面的示例中,我们使用500毫秒超时创建一个请求,另一个请求使用3000毫秒超时。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
        .build();

    try {
      // Copy to customize OkHttp for this request.
      OkHttpClient copy = client.newBuilder()
          .readTimeout(500, TimeUnit.MILLISECONDS)
          .build();

      Response response = copy.newCall(request).execute();
      System.out.println("Response 1 succeeded: " + response);
    } catch (IOException e) {
      System.out.println("Response 1 failed: " + e);
    }

    try {
      // Copy to customize OkHttp for this request.
      OkHttpClient copy = client.newBuilder()
          .readTimeout(3000, TimeUnit.MILLISECONDS)
          .build();

      Response response = copy.newCall(request).execute();
      System.out.println("Response 2 succeeded: " + response);
    } catch (IOException e) {
      System.out.println("Response 2 failed: " + e);
    }
  }

第十二:取消一个Call

使用Call.cancel()立即停止正在进行的呼叫。 如果线程当前正在写请求或读取响应,它将接收到IOException。
使用此选项可在不再需要呼叫时节省网络; 例如当您的用户导航离开应用程序时。 同步和异步调用都可以取消。

  private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
        .build();

    final long startNanos = System.nanoTime();
    final Call call = client.newCall(request);

    // Schedule a job to cancel the call in 1 second.
    executor.schedule(new Runnable() {
      @Override public void run() {
        System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
        call.cancel();
        System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
      }
    }, 1, TimeUnit.SECONDS);

    try {
      System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
      Response response = call.execute();
      System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
          (System.nanoTime() - startNanos) / 1e9f, response);
    } catch (IOException e) {
      System.out.printf("%.2f Call failed as expected: %s%n",
          (System.nanoTime() - startNanos) / 1e9f, e);
    }
  }

使用方法:

在Activity的onDestroy回调中取消该界面中所有请求,这里需要明确一点,
本篇文章的网络层是OkHttp,既然选择了OkHttp,如果要在onDestroy中取消未开始需要
网络请求。比较明智的做法是以该Activity的上下文的hash值作为tag。取消请求时将hash值传入,则该界面所有的请求都可以取消。

public void cancelCallsWithTag(Object tag) {  
    if (tag == null) {  
        return;  
    }  

    synchronized (mOkHttpClient.dispatcher().getClass()) {  
      for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {  
            if (tag.equals(call.request().tag())) call.cancel();  
        }  

     for (Call call : mOkHttpClient.dispatcher().runningCalls()) {  
            if (tag.equals(call.request().tag())) call.cancel();  
        }  
    }  
}  

第十三:处理验证

OkHttp可以自动重试未经身份验证的请求。 当响应为401未授权时,将要求Authenticator提供凭据。 实现应构建包含缺少凭据的新请求。
如果没有可用的凭据,则返回null以跳过重试。
使用Response.challenges()获取任何身份验证挑战的方案和领域。 当完成基本挑战时,使用Credentials.basic(username,password)
对请求标头进行编码。

  private final OkHttpClient client;

  public Authenticate() {
    client = new OkHttpClient.Builder()
        .authenticator(new Authenticator() {
          @Override public Request authenticate(Route route, Response response) throws IOException {
            System.out.println("Authenticating for response: " + response);
            System.out.println("Challenges: " + response.challenges());
            String credential = Credentials.basic("jesse", "password1");
            return response.request().newBuilder()
                .header("Authorization", credential)
                .build();
          }
        })
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/secrets/hellosecret.txt")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

  为了避免在身份验证不起作用时进行许多重试,可以返回null放弃。 例如,您可能希望在尝试这些确切的凭据时跳过重试:
   if (credential.equals(response.request().header("Authorization"))) {
    return null; // If we already failed with these credentials, don't retry.
   }

  当您达到应用程式定义的尝试上限时,您也可以略过重试:
  if (responseCount(response) >= 3) {
    return null; // If we've failed 3 times, give up.
  }

  以上代码依赖于此responseCount()方法:
  private int responseCount(Response response) {
    int result = 1;
    while ((response = response.priorResponse()) != null) {
      result++;
    }
    return result;
  }

第十四:提取响应头

典型的HTTP头 像是一个Map每个字段都有一个或没有值。但是一些头允许多个值,像Guava的Multimap。例如:HTTP响应里面提供的Vary响应头,就是多值的。OkHttp的api试图让这些情况都适用。当写请求头的时候,使用header(name, value)可以设置唯一的name、value。如果已经有值,旧的将被移除,然后添加新的。使用addHeader(name, value)可以添加多值(添加,不移除已有的)。当读取响应头时,使用header(name)返回最后出现的name、value。通常情况这也是唯一的name、value。如果没有值,那么header(name)将返回null。如果想读取字段对应的所有值,使用headers(name)会返回一个list。为了获取所有的Header,Headers类支持按index访问。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://api.github.com/repos/square/okhttp/issues")
        .header("User-Agent", "OkHttp Headers.java")
        .addHeader("Accept", "application/json; q=0.5")
        .addHeader("Accept", "application/vnd.github.v3+json")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println("Server: " + response.header("Server"));
    System.out.println("Date: " + response.header("Date"));
    System.out.println("Vary: " + response.headers("Vary"));
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值