OkHttp使用详解

概要

本篇主要讲解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

//步骤1
OkHttpClient client = new OkHttpClient();

//步骤2
Request request = new Request.Builder().url(url).build();

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

//步骤3
client.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);
  }
  
});
//步骤3
client.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

//步骤1
OkHttpClient client = new OkHttpClient();

//步骤2
RequestBody formBody = new FormBody.Builder().add("name", "木子").add("pwd", "123456").build();
Request request = new Request.Builder().url(uri).post(formBody).build();

//步骤3
client.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();
  }

});
//步骤1
OkHttpClientclient = new OkHttpClient();
 
//步骤2
RequestBodyformBody = new FormBody.Builder().add("name", "木子").add("pwd", "123456").build();
Requestrequest = new Request.Builder().url(uri).post(formBody).build();
 
//步骤3
client.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 在响应头中获取请求成功后的Cookie
public void onResponse(Call call, Response response) throws IOException {
  String result = response.body().string();
  cookie=response.header("Set-Cookie");
}

//List请求时携带客户端Login成功后Cookie
Request request = new Request.Builder().url(url).addHeader("Cookie", cookie).build();
//Login 在响应头中获取请求成功后的Cookie
public void onResponse(Callcall, Responseresponse) throws IOException {
 String result = response.body().string();
 cookie=response.header("Set-Cookie");
}
 
//List请求时携带客户端Login成功后Cookie
Requestrequest = 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中使用CookieJar
OkHttpClient 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中使用CookieJar
OkHttpClientclient = 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,代码就不再介绍了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值