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

OkHttp使用详解一

2017-04-11 15:38 218 查看

概要

本篇主要讲解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方式构建出来的。

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


2、 通过Request.Builder对象新建一个Request对象,仍然使用的是建造者模式;

3、 返回执行结果,同步请求和异步请求在这里才有区别,在执行请求之前,上面两个步骤都是一样的。

OkHttp同步请求

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


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

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


OkHttp异步请求

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

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

});


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

//新建一个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);
}
});
}
});


get请求url转码

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

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


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

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 + '}';
}
}


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

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来设置表单的参数,示例代码如下:

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

});


OkHttp中Cookie操作

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

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

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


2、客户端带着Cookie访问ListServlet,在ListServlet中检查Cookie中isLogin是否为true,若为true则返回成功结果,否则返回错误信息,ListServlet核心处理代码如下:

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


OkHttp操作Cookie方式一

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

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


OkHttp操作Cookie方式二

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

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


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

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


结束语

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

本文地址www.sunnyang.com/364.html

You can set your CookieStore in the OkHttp client with the following code:

OkHttpClient client = new OkHttpClient();
client.setCookieHandler(new CookieManager(
new PersistentCookieStore(getApplicationContext()),
CookiePolicy.ACCEPT_ALL));


Putting the code into a custom Application’s onCreate function solves the issue. It works now.

public class MyApplication extends Application {
public void onCreate() {
super.onCreate();

// enable cookies
java.net.CookieManager cookieManager = new java.net.CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cookieManager);
}
}


I managed to get persistent cookies with okhttp with the following CookieStore, which is partly copied from the one of android-async-http. It works with API lvl 9 and maybe fewer.

import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;

import java.io.*;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
* A persistent cookie store which implements the Apache HttpClient CookieStore interface.
* Cookies are stored and will persist on the user's device between application sessions since they
* are serialized and stored in SharedPreferences. Instances of this class are
* designed to be used with AsyncHttpClient#setCookieStore, but can also be used with a
* regular old apache HttpClient/HttpContext if you prefer.
*/
public class PersistentCookieStore implements CookieStore {

private static final String LOG_TAG = "PersistentCookieStore";
private static final String COOKIE_PREFS = "CookiePrefsFile";
private static final String COOKIE_NAME_PREFIX = "cookie_";

private final HashMap<String, ConcurrentHashMap<String, HttpCookie>> cookies;
private final SharedPreferences cookiePrefs;

/**
* Construct a persistent cookie store.
*
* @param context Context to attach cookie store to
*/
public PersistentCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
cookies = new HashMap<String, ConcurrentHashMap<String, HttpCookie>>();

// Load any previously stored cookies into the store
Map<String, ?> prefsMap = cookiePrefs.getAll();
for(Map.Entry<String, ?> entry : prefsMap.entrySet()) {
if (((String)entry.getValue()) != null && !((String)entry.getValue()).startsWith(COOKIE_NAME_PREFIX)) {
String[] cookieNames = TextUtils.split((String)entry.getValue(), ",");
for (String name : cookieNames) {
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if (encodedCookie != null) {
HttpCookie decodedCookie = decodeCookie(encodedCookie);
if (decodedCookie != null) {
if(!cookies.containsKey(entry.getKey()))
cookies.put(entry.getKey(), new ConcurrentHashMap<String, HttpCookie>());
cookies.get(entry.getKey()).put(name, decodedCookie);
}
}
}

}
}
}

@Override
public void add(URI uri, HttpCookie cookie) {
String name = getCookieToken(uri, cookie);

// Save cookie into local store, or remove if expired
if (!cookie.hasExpired()) {
if(!cookies.containsKey(uri.getHost()))
cookies.put(uri.getHost(), new ConcurrentHashMap<String, HttpCookie>());
cookies.get(uri.getHost()).put(name, cookie);
} else {
if(cookies.containsKey(uri.toString()))
cookies.get(uri.getHost()).remove(name);
}

// Save cookie into persistent store
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableHttpCookie(cookie)));
prefsWriter.commit();
}

protected String getCookieToken(URI uri, HttpCookie cookie) {
return cookie.getName() + cookie.getDomain();
}

@Override
public List<HttpCookie> get(URI uri) {
ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>();
if(cookies.containsKey(uri.getHost()))
ret.addAll(cookies.get(uri.getHost()).values());
return ret;
}

@Override
public boolean removeAll() {
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.commit();
cookies.clear();
return true;
}

@Override
public boolean remove(URI uri, HttpCookie cookie) {
String name = getCookieToken(uri, cookie);

if(cookies.containsKey(uri.getHost()) && cookies.get(uri.getHost()).containsKey(name)) {
cookies.get(uri.getHost()).remove(name);

SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
if(cookiePrefs.contains(COOKIE_NAME_PREFIX + name)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
}
prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
prefsWriter.commit();

return true;
} else {
return false;
}
}

@Override
public List<HttpCookie> getCookies() {
ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>();
for (String key : cookies.keySet())
ret.addAll(cookies.get(key).values());

return ret;
}

@Override
public List<URI> getURIs() {
ArrayList<URI> ret = new ArrayList<URI>();
for (String key : cookies.keySet())
try {
ret.add(new URI(key));
} catch (URISyntaxException e) {
e.printStackTrace();
}

return ret;
}

/**
* Serializes Cookie object into String
*
* @param cookie cookie to be encoded, can be null
* @return cookie encoded as String
*/
protected String encodeCookie(SerializableHttpCookie cookie) {
if (cookie == null)
return null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in encodeCookie", e);
return null;
}

return byteArrayToHexString(os.toByteArray());
}

/**
* Returns cookie decoded from cookie string
*
* @param cookieString string of cookie as returned from http request
* @return decoded cookie or null if exception occured
*/
protected HttpCookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
HttpCookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie();
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
}

return cookie;
}

/**
* Using some super basic byte array <-> hex conversions so we don't have to rely on any
* large Base64 libraries. Can be overridden if you like!
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
protected String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase(Locale.US);
}

/**
* Converts hex values from strings to byte arra
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
protected byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}


The SerializableHttpCookie.java:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.HttpCookie;

public class SerializableHttpCookie implements Serializable {
private static final long serialVersionUID = 6374381323722046732L;

private transient final HttpCookie cookie;
private transient HttpCookie clientCookie;

public SerializableHttpCookie(HttpCookie cookie) {
this.cookie = cookie;
}

public HttpCookie getCookie() {
HttpCookie bestCookie = cookie;
if (clientCookie != null) {
bestCookie = clientCookie;
}
return bestCookie;
}

private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookie.getName());
out.writeObject(cookie.getValue());
out.writeObject(cookie.getComment());
out.writeObject(cookie.getCommentURL());
out.writeObject(cookie.getDomain());
out.writeLong(cookie.getMaxAge());
out.writeObject(cookie.getPath());
out.writeObject(cookie.getPortlist());
out.writeInt(cookie.getVersion());
out.writeBoolean(cookie.getSecure());
out.writeBoolean(cookie.getDiscard());
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
String name = (String) in.readObject();
String value = (String) in.readObject();
clientCookie = new HttpCookie(name, value);
clientCookie.setComment((String) in.readObject());
clientCookie.setCommentURL((String) in.readObject());
clientCookie.setDomain((String) in.readObject());
clientCookie.setMaxAge(in.readLong());
clientCookie.setPath((String) in.readObject());
clientCookie.setPortlist((String) in.readObject());
clientCookie.setVersion(in.readInt());
clientCookie.setSecure(in.readBoolean());
clientCookie.setDiscard(in.readBoolean());
}
}


转自:http://stackoverflow.com/questions/25461792/persistent-cookie-store-using-okhttp-2-on-android
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android cookie