OkHttp使用详解

来源:互联网 发布:淘宝货到付款可以退吗 编辑:程序博客网 时间:2024/06/04 19:08

概要

本篇主要讲解OkHttp最基本的使用,如最常见的get和post请求,在本文中post主要介绍的是表单提交方式的请求,文章最后介绍了如何在服务端和客户端设置处理Cookie,客户端给出了两种最常见的方式处理Cookie。

在学习Android的过程中,官方集成网络框架就包含了HttpUrlConnection、HttpClient、Volley,其中Volley是android开发团队在2013年Google I/O大会上推出了一个新的网络通信框架,目前Volley中部分代码仍然借助于HttpClient中部分功能,然而HttpClient在Android最新版本6.0中已经被剔除掉了,如果想要使用Volley还必须使用一个第三方的jai包org.apache.http.legacy.jar,再者Volley是针对数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。如果开发中使用HttpUrlConnection则要从头开始封装对应得操作,所以最近转向了一个第三方网络请求框架OkHttp,本文所介绍的示例都是针对OkHttp3.0库。

OkHttp

OkHttp是一个 Java 的 HTTP+SPDY 客户端开发包,同时也支持 Android。需要Android 2.3以上, OkHttp github源码 ,同时还需要一个 okio包

  • OKHttp是Android版Http客户端。非常高效,支持SPDY、连接池、GZIP和 HTTP 缓存;
  • 默认情况下,OKHttp会自动处理常见的网络问题,像二次连接、SSL的握手问题;
  • 如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求;
  • 从Android4.4开始HttpURLConnection的底层实现采用的是okHttp。

OkHttp的简单使用

OkHttp建立一个网络请求可以是异步的也可以是同步的,大体上分为三个步骤:

  1. 新建一个OkHttpClient对象,可以直接new一个OkHttpClient,也可以使用建造者模式build一个,事实上new一个新的内部也是调用的build方式构建出来的。 

    Java

    public OkHttpClient() {  this(new Builder());}
    public OkHttpClient() { this(new Builder());}
  2. 通过Request.Builder对象新建一个Request对象,仍然使用的是建造者模式;
  3. 返回执行结果,同步请求和异步请求在这里才有区别,在执行请求之前,上面两个步骤都是一样的。

OkHttp同步请求

Java

//步骤1OkHttpClient client = new OkHttpClient();//步骤2Request request = new Request.Builder().url(url).build();//步骤3Response response = client.newCall(request).execute();if(response.isSuccessful()){  String result = response.body().string();  System.out.println(result);}
//步骤1OkHttpClientclient = new OkHttpClient(); //步骤2Requestrequest = new Request.Builder().url(url).build(); //步骤3Responseresponse = client.newCall(request).execute();if(response.isSuccessful()){ String result = response.body().string(); System.out.println(result);}

这里建立的默认请求采用的是get方式请求的,源码如下:

Java

public Builder() {  this.method = "GET";  this.headers = new Headers.Builder();}
public Builder() {  this.method = "GET";  this.headers = new Headers.Builder();}

OkHttp异步请求

与同步请求不同之处就在步骤三中,步骤三代码如下:

Java

//步骤3client.newCall(request).enqueue(new Callback() {  @Override  public void onFailure(Call call, IOException e) {      }  @Override  public void onResponse(Call call, Response response) throws IOException {    String result = response.body().string();    System.out.println(result);  }  });
//步骤3client.newCall(request).enqueue(new Callback() {  @Override public void onFailure(Callcall, IOException e) {  }  @Override public void onResponse(Callcall, Responseresponse) throws IOException { String result = response.body().string(); System.out.println(result); } });

注意:虽然这里是异步请求,但是这里的回调函数确是在子线程中执行的回调,在Android开发中如果想将回调放入主线程中,因为只有在主线程中才能更新UI,我们一般会借助于Handler消息机制来处理,以前写过一篇文章Handler机制,一般Looper在哪个线程,它处理的消息就在哪个线程。

Java

//新建一个Handler实例Handler handler = new Handler(Looper.getMainLooper());//自定义一个接口回调public interface RequestCallback {  void onStart();  void onSuccess(String result);  void onFailure(ErrorType errorType);}//设置转换为主线程的回调httpClient.newCall(request).enqueue(new Callback() {  public void onResponse(Response response) throws IOException {    if (response.isSuccessful()) {      final String result = response.body().string();      handler.post(new Runnable() {        public void run() {          callback.onSuccess(result);        }      });    }  }  public void onFailure(Request req, final IOException e) {    handler.post(new Runnable() {      public void run() {        callback.onFailure(ErrorType.SERVER);      }    });  }});
//新建一个Handler实例Handlerhandler = new Handler(Looper.getMainLooper());//自定义一个接口回调public interface RequestCallback {  void onStart();  void onSuccess(String result);  void onFailure(ErrorTypeerrorType); }//设置转换为主线程的回调httpClient.newCall(request).enqueue(new Callback() { public void onResponse(Responseresponse) throws IOException { if (response.isSuccessful()) { final String result = response.body().string(); handler.post(new Runnable() { public void run() { callback.onSuccess(result); } }); } }  public void onFailure(Requestreq, final IOException e) { handler.post(new Runnable() { public void run() { callback.onFailure(ErrorType.SERVER); } }); }});

get请求url转码

在处理get请求是我们要对url中需要传入的参数进行Encoder处理,主要是为了处理传输过程中的特殊字符如中文或者空格等。

Java

public static String buildQueryUrl(String uri, List<KeyValue> params) {  StringBuilder queryBuilder = new StringBuilder(uri);  if (!uri.contains("?")) {    queryBuilder.append("?");  } else if (!uri.endsWith("?")) {    queryBuilder.append("&");  }  List<KeyValue> queryParams = params;  if (queryParams != null) {    for (KeyValue kv : queryParams) {      String name = kv.key;      String value = kv.getValueStr();      if (!TextUtils.isEmpty(name) && value != null) {        queryBuilder.append(Uri.encode(name, "utf-8")).append("=").append(Uri.encode(value, "utf-8")).append("&");      }    }  }  if (queryBuilder.charAt(queryBuilder.length() - 1) == '&') {    queryBuilder.deleteCharAt(queryBuilder.length() - 1);  }  if (queryBuilder.charAt(queryBuilder.length() - 1) == '?') {    queryBuilder.deleteCharAt(queryBuilder.length() - 1);  }  return queryBuilder.toString();}
public static String buildQueryUrl(String uri, List<KeyValue> params) { StringBuilderqueryBuilder = new StringBuilder(uri); if (!uri.contains("?")) { queryBuilder.append("?"); } else if (!uri.endsWith("?")) { queryBuilder.append("&"); } List<KeyValue> queryParams = params; if (queryParams != null) { for (KeyValuekv : queryParams) { String name = kv.key; String value = kv.getValueStr(); if (!TextUtils.isEmpty(name) && value != null) { queryBuilder.append(Uri.encode(name, "utf-8")).append("=").append(Uri.encode(value, "utf-8")).append("&"); } } }  if (queryBuilder.charAt(queryBuilder.length() - 1) == '&') { queryBuilder.deleteCharAt(queryBuilder.length() - 1); }  if (queryBuilder.charAt(queryBuilder.length() - 1) == '?') { queryBuilder.deleteCharAt(queryBuilder.length() - 1); } return queryBuilder.toString();}

其中KeyValue就是一个JavaBean,也可以中Map代替,本文KeyValue代码来自xUtils3。

Java

public class KeyValue {    public final String key;    public final Object value;    public KeyValue(String key, Object value) {        this.key = key;        this.value = value;    }    public String getValueStr() {        return value == null ? null : value.toString();    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        KeyValue keyValue = (KeyValue) o;        return key == null ? keyValue.key == null : key.equals(keyValue.key);    }    @Override    public int hashCode() {        return key != null ? key.hashCode() : 0;    }    @Override    public String toString() {        return "KeyValue{" + "key='" + key + '\'' + ", value=" + value + '}';    }}
public class KeyValue {    public final String key;    public final Object value;     public KeyValue(String key, Object value) {        this.key = key;        this.value = value;    }     public String getValueStr() {        return value == null ? null : value.toString();    }     @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;         KeyValuekeyValue = (KeyValue) o;         return key == null ? keyValue.key == null : key.equals(keyValue.key);     }     @Override    public int hashCode() {        return key != null ? key.hashCode() : 0;    }     @Override    public String toString() {        return "KeyValue{" + "key='" + key + '\'' + ", value=" + value + '}';    }}

下面是一个url简单的示例:

Java

List<KeyValue> list = new ArrayList<KeyValue>();list.add(new KeyValue("name", "木子"));list.add(new KeyValue("pwd", "123456"));String uri = "http://192.168.0.117:8080/cookie/get?isLogin=true";uri = ParamUtils.buildQueryUrl(uri, list);
List<KeyValue> list = new ArrayList<KeyValue>();list.add(new KeyValue("name", "木子"));list.add(new KeyValue("pwd", "123456"));String uri = "http://192.168.0.117:8080/cookie/get?isLogin=true";uri = ParamUtils.buildQueryUrl(uri, list);

转码后输入结果为:

http://192.168.0.117:8080/cookie/get?isLogin=true&amp;name=%E6%9C%A8%E5%AD%90&amp;pwd=123456

post请求

这里所讲的post请求,仅仅限制在表单提交中,也就是content-type为” application/x-www-form-urlencoded”的请求,其余的方式如文件上传下次再做介绍。对于post方式提交表单,OkHttp已经封装好了相应的类FormBody来设置表单的参数,示例代码如下:

Java

//步骤1OkHttpClient client = new OkHttpClient();//步骤2RequestBody formBody = new FormBody.Builder().add("name", "木子").add("pwd", "123456").build();Request request = new Request.Builder().url(uri).post(formBody).build();//步骤3client.newCall(request).enqueue(new Callback() {  @Override  public void onFailure(Call call, IOException e) {  }  @Override  public void onResponse(Call call, Response response) throws IOException {    String result = response.body().string();  }});
//步骤1OkHttpClientclient = new OkHttpClient(); //步骤2RequestBodyformBody = new FormBody.Builder().add("name", "木子").add("pwd", "123456").build();Requestrequest = new Request.Builder().url(uri).post(formBody).build(); //步骤3client.newCall(request).enqueue(new Callback() {  @Override public void onFailure(Callcall, IOException e) {  } @Override public void onResponse(Callcall, Responseresponse) throws IOException { String result = response.body().string(); } });

OkHttp中Cookie操作

先介绍一下接下来demo的操作流程

  1. 1、 访问LoginServlet,然后服务器端返回Cookie,在Cookie中设置了isLogin=true,LoginServlet核心处理代码如下: 

    Java

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  resp.setCharacterEncoding("utf-8");  PrintWriter out=resp.getWriter();  //設置cookie,返回isLogin为true  Cookie cookie=new Cookie("isLogin", "true");  resp.addCookie(cookie);  out.println("login success!");    out.flush();  out.close();}
    protected void doPost(HttpServletRequestreq, HttpServletResponseresp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); PrintWriterout=resp.getWriter(); //設置cookie,返回isLogin为true Cookiecookie=new Cookie("isLogin", "true"); resp.addCookie(cookie); out.println("login success!");  out.flush(); out.close();}
  2. 客户端带着Cookie访问ListServlet,在ListServlet中检查Cookie中isLogin是否为true,若为true则返回成功结果,否则返回错误信息,ListServlet核心处理代码如下: 

    Java

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  resp.setCharacterEncoding("utf-8");  PrintWriter out = resp.getWriter();  // 获取所有的cookie值  Cookie[] cookies = req.getCookies();  if(cookies==null||cookies.length==0){    out.println("list  error!");    return;  }  Cookie cookie = null;  for (int i = 0; i < cookies.length; i++) {    cookie = cookies[i];    if (cookie.getName().equals("isLogin")) {      if(cookie.getValue().equals("true")){        resp.addCookie(cookie);        out.println("list success!");      }else{        out.println("list  error!");      }      break;    }  }  out.flush();  out.close();}
    protected void doPost(HttpServletRequestreq, HttpServletResponseresp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); PrintWriterout = resp.getWriter(); // 获取所有的cookie值 Cookie[] cookies = req.getCookies(); if(cookies==null||cookies.length==0){ out.println("list  error!"); return; } Cookiecookie = null; for (int i = 0; i < cookies.length; i++) { cookie = cookies[i]; if (cookie.getName().equals("isLogin")) { if(cookie.getValue().equals("true")){ resp.addCookie(cookie); out.println("list success!"); }else{ out.println("list  error!"); } break; } } out.flush(); out.close();}

OkHttp操作Cookie方式一

该种方式是通过Http请求中的Header来处理Cookie的,服务器端返回的相应头中会有Set-Cookie字段,客户端请求时携带Cookie可以设置Cookie字段。

Java

//Login 在响应头中获取请求成功后的Cookiepublic void onResponse(Call call, Response response) throws IOException {  String result = response.body().string();  cookie=response.header("Set-Cookie");}//List请求时携带客户端Login成功后CookieRequest request = new Request.Builder().url(url).addHeader("Cookie", cookie).build();
//Login 在响应头中获取请求成功后的Cookiepublic void onResponse(Callcall, Responseresponse) throws IOException { String result = response.body().string(); cookie=response.header("Set-Cookie");} //List请求时携带客户端Login成功后CookieRequestrequest = new Request.Builder().url(url).addHeader("Cookie", cookie).build();

OkHttp操作Cookie方式二

事实上OkHttp提供了一种便捷的方式来操作Cookie,通过实现CookieJar接口来自动管理Cookie。

Java

public final class JavaNetCookieJar implements CookieJar {  private final List<Cookie> allCookies=new ArrayList<Cookie>();  @Override  public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {    allCookies.addAll(cookies);  }  @Override  public synchronized List<Cookie> loadForRequest(HttpUrl url) {    List<Cookie> result = new ArrayList<Cookie>();    for (Cookie cookie : allCookies) {      if (cookie.matches(url)) {        result.add(cookie);      }    }    return result;  }}//OkHttp中使用CookieJarOkHttpClient client = new OkHttpClient.Builder().cookieJar(new JavaNetCookieJar()).build();
public final class JavaNetCookieJar implements CookieJar { private final List<Cookie> allCookies=new ArrayList<Cookie>();  @Override public synchronized void saveFromResponse(HttpUrlurl, List<Cookie> cookies) { allCookies.addAll(cookies); }  @Override public synchronized List<Cookie> loadForRequest(HttpUrlurl) { List<Cookie> result = new ArrayList<Cookie>(); for (Cookiecookie : allCookies) { if (cookie.matches(url)) { result.add(cookie); } } return result; }}//OkHttp中使用CookieJarOkHttpClientclient = new OkHttpClient.Builder().cookieJar(new JavaNetCookieJar()).build();

通过上面方式就可以自定管理Cookie了,但是有一个问题,如果我们客户端使用了多个OkHttpClient,由于CookieJar并不是一个静态属性,所以每一个OkHttpClient都会有一个自己的CookieJar实例,如果已经登录成功后使用一个新的OkHttpClient会发现CookieJar中Cookie为null,当然了如果一个App只有一个OkHttpClient不会出现该问题,解决该问题也很简单,就是设置一个全局的静态变量,每次请求成功后都将Cookie相关信息赋值给该变量就可以了,加入全局静态变量后代码如下:

Java

public final class JavaNetCookieJar implements CookieJar {  private final List<Cookie> allCookies=new ArrayList<Cookie>();  @Override  public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {    allCookies.addAll(cookies);    if(cookies!=null){      //全局静态变量ALL_COOKIES      NetUtils.ALL_COOKIES=allCookies;    }  }  @Override  public synchronized List<Cookie> loadForRequest(HttpUrl url) {    List<Cookie> result = new ArrayList<Cookie>();    for (Cookie cookie : allCookies) {      if (cookie.matches(url)) {        result.add(cookie);      }    }    if(result.size()==0&&NetUtils.ALL_COOKIES!=null){      result=NetUtils.ALL_COOKIES;    }    return result;  }}
public final class JavaNetCookieJar implements CookieJar { private final List<Cookie> allCookies=new ArrayList<Cookie>();  @Override public synchronized void saveFromResponse(HttpUrlurl, List<Cookie> cookies) { allCookies.addAll(cookies); if(cookies!=null){ //全局静态变量ALL_COOKIES NetUtils.ALL_COOKIES=allCookies; } }  @Override public synchronized List<Cookie> loadForRequest(HttpUrlurl) { List<Cookie> result = new ArrayList<Cookie>(); for (Cookiecookie : allCookies) { if (cookie.matches(url)) { result.add(cookie); } } if(result.size()==0&&NetUtils.ALL_COOKIES!=null){ result=NetUtils.ALL_COOKIES; } return result; }}

结束语

本文只讲解了OkHttp的一小部分应用,接下来文件上传和下载还有请求中缓存cache的处理还没有设计,在以后的文章中再详细的介绍。有关Cookie的介绍,在网上也有许多代码,有些将Cookie持久化到了SharedPreferences,有兴趣的可以查看下面这个链接 http://stackoverflow.com/questions/25461792/persistent-cookie-store-using-okhttp-2-on-android ,在本文中也可以在CookieJar的实例中来持久化Cookie,代码就不再介绍了。

0 0
原创粉丝点击