您的位置:首页 > 理论基础 > 计算机网络

OkHttp使用详解

2016-09-18 19:53 260 查看


概要

本篇主要讲解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建立一个网络请求可以是异步的也可以是同步的,大体上分为三个步骤:
新建一个OkHttpClient对象,可以直接new一个OkHttpClient,也可以使用建造者模式build一个,事实上new一个新的内部也是调用的build方式构建出来的。 

Java

public OkHttpClient() {
this(new Builder());
}

public OkHttpClient() {
this(new Builder());
}


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


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(res
19941
ponse.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&name=%E6%9C%A8%E5%AD%90&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、 访问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();
}


客户端带着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,代码就不再介绍了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: