Android_OkHttp简介

来源:互联网 发布:让脸变搞笑的软件 编辑:程序博客网 时间:2024/05/29 11:09

一、Android网络通信

Android系统提供了两种HTTP通信类,HttpURLConnection和HttpClient。
关于HttpURLConnection和HttpClient的选择>>官方博客:http://android-developers.blogspot.com/2011/09/androids-http-clients.html


利用原生的这两种方式编写网络代码,需要自己考虑很多,网络加载获取数据或许可以,但是要将手机本地数据上传至网络,根据不同的web端接口,需要组织不同的数据内容上传,就会有很大的工作需要做。因此出现了很多开源的框架。
age:
android-async-http:https://github.com/loopj/android-async-http
它是专门针对Android在Apache的HttpClient基础上构建的异步的callback-based http client。所有的请求全在UI线程之外发生,而callback发生在创建它的线程中,应用了Android的Handler发送消息机制。你也可以把AsyncHttpClient应用在Service中或者后台线程中,库代码会自动识别出它所运行的context。


Http Request:https://github.com/kevinsawicki/http-request
HttpRequest就是一种简单粗暴,一个请求可以涵盖在一句代码当中,不给任何一点冗余的成分。不过它在2015年就停止更新了,因此现在基本很少使用!


okhttp:https://github.com/square/okhttp
okhttp更为强大,相对而言,使用方式稍显复杂,可是是目前最为流行的网络加载框架。而且这是著名公司Square的开源框架,出手不凡。


二、okhttp的简介


Google在大部分安卓版本中推荐使用HttpURLConnection,可是这个类相比HttpClient很难使用,OkHttp是一个相对成熟的解决方案,在Android4.4的源码中可以看到HttpURLConnection已经替换成OkHttp实现了。所以OkHttp还是很强大的

OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。

使用 OkHttp 无需重写您程序中的网络代码。OkHttp实现了几乎和java.net.HttpURLConnection一样的API。如果你用了 Apache HttpClient,则OkHttp也提供了一个对应的okhttp-apache 模块。


okhttp官方资料
官方介绍:http://square.github.io/okhttp/
github源码:https://github.com/square/okhttp

使用范围
OkHttp支持Android 2.3及其以上版本。
对于Java, JDK1.7以上。

jar包准备
使用OKHttp我们就需要OKHttp的jar包,同时由于OKHttp依赖于Okio,我们也需要下载Okio的jar包。官方的介绍界面中也有的下载:
OkHttp:http://download.csdn.net/detail/two_water/9631536
Okio:http://download.csdn.net/detail/two_water/9631637


三、okhttp的API

Http 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();        if (response.isSuccessful()) {                return response.body().string();    } else {                throw new IOException("Unexpected code " + response);    }}
Request是OkHttp中访问的请求,Builder是辅助类。Response即OkHttp中的响应。

使用response.body()返回ResponseBody类,可以方便的获取string,当然也能获取到流的形式:
public final InputStream byteStream()


Http Post 提交Json数据

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();    f (response.isSuccessful()) {        return response.body().string();    } else {        throw new IOException("Unexpected code " + response);    }}

Http Post提交键值对的数据

OkHttpClient client = new OkHttpClient();String post(String url, String json) throws IOException {      RequestBody formBody = new FormEncodingBuilder()    .add("platform", "android")    .add("name", "bug")    .add("subject", "XXXXXXXXXXXXXXX")    .build();       Request request = new Request.Builder()      .url(url)      .post(body)      .build();       Response response = client.newCall(request).execute();    if (response.isSuccessful()) {        return response.body().string();    } else {        throw new IOException("Unexpected code " + response);    }}


同步Get

下载一个文件,打印他的响应头,以string形式打印响应体。响应体的 string() ((response.body().string())方法对于小文档来说十分方便、高效。但是如果响应体太大(超过1MB),应避免适应 string()方法 ,因为他会将把整个文档加载到内存中,可能会造成OOM,因此对于超过1MB的响应body,应使用流的方式来处理body

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());}


异步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(Request request, Throwable throwable) {        throwable.printStackTrace();      }       @Override public void onResponse(Response response) throws IOException {        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());      }    });}

提取响应头和给响应头添加多值

典型的HTTP头 像是一个 Map<String, String> :每个字段都有一个或没有值。但是一些头允许多个值,像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"));}


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 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 {    File file = new File("README.md");     Request request = new Request.Builder()        .url("https://api.github.com/markdown/raw")        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))        .build();     Response response = client.newCall(request).execute();    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);     System.out.println(response.body().string());}


Post提交表单

使用FormEncodingBuilder来构建和HTML<form>标签相同效果的请求体。键值对将使用一种HTML兼容形式的URL编码来进行编码。

private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception {    RequestBody formBody = new FormEncodingBuilder()        .add("search", "Jurassic Park")        .build();    Request request = new Request.Builder()        .url("https://en.wikipedia.org/w/index.php")        .post(formBody)        .build();     Response response = client.newCall(request).execute();    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);     System.out.println(response.body().string());}

Post方式提交分块请求

MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。

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 MultipartBuilder()        .type(MultipartBuilder.FORM)        .addPart(            Headers.of("Content-Disposition", "form-data; name=\"title\""),            RequestBody.create(null, "Square Logo"))        .addPart(            Headers.of("Content-Disposition", "form-data; name=\"image\""),            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());}

取消一个Call

使用Call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException。当call没有必要的时候,使用这个api可以节约网络资源。例如当用户离开一个应用时。不管同步还是异步的call都可以取消。
你可以通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。

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);    }}

超时

没有响应时使用超时结束call。没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。

private final OkHttpClient client; public ConfigureTimeouts() throws Exception {    client = new OkHttpClient();    client.setConnectTimeout(10, TimeUnit.SECONDS);    client.setWriteTimeout(10, TimeUnit.SECONDS);    client.setReadTimeout(30, TimeUnit.SECONDS);} 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();     Response response = client.newCall(request).execute();    System.out.println("Response completed: " + response);}


每个Call的配置

使用OkHttpClient,所有的HTTP Client配置包括代理设置、超时设置、缓存设置。当你需要为单个call改变配置的时候,clone 一个 OkHttpClient。这个api将会返回一个浅拷贝(shallow copy),你可以用来单独自定义。下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。

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 {      Response response = client.clone() // Clone to make a customized OkHttp for this request.          .setReadTimeout(500, TimeUnit.MILLISECONDS)          .newCall(request)          .execute();      System.out.println("Response 1 succeeded: " + response);    } catch (IOException e) {      System.out.println("Response 1 failed: " + e);    }     try {      Response response = client.clone() // Clone to make a customized OkHttp for this request.          .setReadTimeout(3000, TimeUnit.MILLISECONDS)          .newCall(request)          .execute();      System.out.println("Response 2 succeeded: " + response);    } catch (IOException e) {      System.out.println("Response 2 failed: " + e);    }}


1 0
原创粉丝点击