Android中在使用OkHttp这个库的时候。有时候须要持久化Cookie,那么怎么实现呢。OkHttp的内部源代码过于复杂,不进行深究。这里仅仅看当中的HttpEngineer里面的部分源代码,在发起请求以及请求结束都会调用这个类的几个方法。

我们先看networkRequest方法。在里面通过client.getCookieHandler()函数获得了CookieHandler对象。通过该对象拿到cookie并设置到请求头里,请求结束后取得响应后通过networkResponse.headers()函数将请求头获得传入receiveHeaders函数,并将取得的cookie存入getCookieHandler得到的一个CookieHandler对象中去

This client doesn't specify a default {@code Accept} header because it
* doesn't know what content types the application is interested in.
*/
private Request networkRequest(Request request) throws IOException {
Request.Builder result = request.newBuilder(); //此处省略n行代码...... CookieHandler cookieHandler = client.getCookieHandler();
if (cookieHandler != null) {
// Capture the request headers added so far so that they can be offered to the CookieHandler.
// This is mostly to stay close to the RI; it is unlikely any of the headers above would
// affect cookie choice besides "Host".
Map> headers = OkHeaders.toMultimap(result.build().headers(), null); Map> cookies = cookieHandler.get(request.uri(), headers); // Add any new cookies to the request.
OkHeaders.addCookies(result, cookies);
} //此处省略n行代码...... return result.build();
}" data-snippet-id="ext.4df17b386793c68468195a690c917859" data-snippet-saved="false" data-csrftoken="bFkHGE1O-cy7WY9D6w70F6qK9YUKZHLynI5g" data-codota-status="done">/**
* Populates request with defaults and cookies.
*
* <p>This client doesn't specify a default {@code Accept} header because it
* doesn't know what content types the application is interested in.
*/
private Request networkRequest(Request request) throws IOException {
Request.Builder result = request.newBuilder(); //此处省略n行代码...... CookieHandler cookieHandler = client.getCookieHandler();
if (cookieHandler != null) {
// Capture the request headers added so far so that they can be offered to the CookieHandler.
// This is mostly to stay close to the RI; it is unlikely any of the headers above would
// affect cookie choice besides "Host".
Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null); Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers); // Add any new cookies to the request.
OkHeaders.addCookies(result, cookies);
} //此处省略n行代码...... return result.build();
}
public void readResponse() throws IOException {
//此处省略n行代码...... receiveHeaders(networkResponse.headers()); //此处省略n行代码......
}
public void receiveHeaders(Headers headers) throws IOException {
CookieHandler cookieHandler = client.getCookieHandler();
if (cookieHandler != null) {
cookieHandler.put(userRequest.uri(), OkHeaders.toMultimap(headers, null));
}
}

而这个CookieHandler对象是OkHttpClient类中的一个属性,提供了getter和setter方法,默认的构造函数OkHttpClient client = new OkHttpClient();不会创建这个CookieHandler对象。如果我们传入了这个对象。那么OkHttp自然会进行cookie的自己主动管理了。

private CookieHandler cookieHandler;
public OkHttpClient setCookieHandler(CookieHandler cookieHandler) {
this.cookieHandler = cookieHandler;
return this;
} public CookieHandler getCookieHandler() {
return cookieHandler;
}

那么如果我们将CookieHandler对象传入

OkHttpClient client = new OkHttpClient();
client.setCookieHandler(CookieHandler cookieHanlder);

那么,如今关键是怎样去实现这个CookieHandler 对象。CookieManager是CookieHandler 的一个子类,其构造函数 public CookieManager(CookieStore store, CookiePolicy cookiePolicy)须要传入两个參数,CookieStore 是一个接口,因此我们实现CookieStore接口中的抽象方法。就可以实现这个CookieHandler 对象。

參考android-async-http这个库,它具有cookie的自己主动管理功能。主要我们參考当中的两个类

參考以上两个类并做适当改动,得到了例如以下两个类,他们的功能就是将cookie保持在SharedPreferences中。

package com.kltz88.okhttp.cookie;

/**
* User:lizhangqu(513163535@qq.com)
* Date:2015-07-13
* Time: 17:31
*/
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());
}
}
> 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>(); // Load any previously stored cookies into the store
Map prefsMap = cookiePrefs.getAll();
for(Map.Entry 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());
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());
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 get(URI uri) {
ArrayList ret = new ArrayList();
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 getCookies() {
ArrayList ret = new ArrayList();
for (String key : cookies.keySet())
ret.addAll(cookies.get(key).values()); return ret;
} @Override
public List getURIs() {
ArrayList ret = new ArrayList();
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 &lt;-&gt; 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 package com.kltz88.okhttp.cookie; /**
* User:lizhangqu(513163535@qq.com)
* Date:2015-07-13
* Time: 17:31
*/
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 &lt;-&gt; 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;
} }

使用的时候。在发起请求前对CookieHandler进行设置,兴许的cookie全都是自己主动管理,无需你关心cookie的保存于读取

OkHttpClient client = new OkHttpClient();

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

这样,实现模拟登录,抓取一些数据就方便非常多了。再也不用手动处理cookie了。

Android OkHttp的Cookie自己主动化管理的更多相关文章

  1. Android自己主动化測试解决方式

    如今,已经有大量的Android自己主动化測试架构或工具可供我们使用,当中包含:Activity Instrumentation, MonkeyRunner, Robotium, 以及Robolect ...

  2. Android自己主动化測试之Monkeyrunner用法及实例

    眼下android SDK里自带的现成的測试工具有monkey 和 monkeyrunner两个.大家别看这俩兄弟名字相像,但事实上是完全然全不同的两个工具,应用在不同的測试领域.总的来说,monke ...

  3. Android Monkey自己主动化測试

    前言 假设你做Android开发,还没有使用过Monkey进行測试,那么今天看到这篇文章,希望能解决你Android測试中的一些问题.起码能帮你省点測试的时间而且发现很多其它的问题. Monkey简单 ...

  4. 使用Adt自带的工具进行Android自己主动化測试(三)

    在这个系列的上一篇文章中,我们介绍了MonkeyRunner,并提到假设依据坐标来编写自己主动化脚本的话存在着一定的局限性(点击文末"阅读原文"能够打开这篇文章查看).这篇文章将进 ...

  5. Android 自己主动化測试(3)&lt;monkeyrunner&gt; 依据ID查找对象&amp;touch&amp;type (python)

    我在之前的两篇文章中用java来实现过 Android 自己主动化測试(1)怎样安装和卸载一个应用(java).Android 自己主动化測试(2)依据ID查找对象(java). 可是本质上都是用mo ...

  6. Android自己主动化构建之Ant多渠道打包实践(下)

    前言 上一篇(Android自己主动化构建之Ant多渠道打包实践(上))已经介绍了Android的apk是怎样构建的,本篇博客继续Ant打包的实践过程. 集成友盟统计SDK 这里以友盟统计为例,对各个 ...

  7. 【金阳光測试】基于控件核心技术探讨---Android自己主动化系列(2)---2013年5月

    第一讲分享了下安卓自己主动化一些概况和一些自己主动化框架现状和技术可以解决什么样的问题. 这次课就深入到android世界里面.遨游.翱翔.深入了解自己主动化測试核心技术. 搞过编程开发的同学听到in ...

  8. 【金阳光測试】大话Android自己主动化測试--Android自己主动化系列(1)--金阳光于2013年4月份

    Android自己主动化測试框架和工具在四年多的发展日趋成熟. 从五年前的第一代自己主动化架构演进到眼下第四代(本系列讲座第7篇后将具体剖析第三代和第四代自己主动化框架)从曾经最早谷歌推崇的monke ...

  9. 自己主动化的在程序中显示SVN版本号

    有时候会有这种情况,策划拿着应用过来提一个bug,但我们却不好确定策划的手机上装的应用相应的是那个代码版本号. 为了解决问题.我们希望能在应用上显示出当前应用所相应的代码版本号,即svn版本号. 构想 ...

随机推荐

  1. 在 IntelliJ IDEA 中配置 JSF 开发环境的入门详解

    JSF 作为 JavaEE 官方标准,在了解并掌握其基本开发技术后,对于功能要求较高.业务流程复杂的各种现代 Web 应用程序开发将会成为非常合适且强大的高效率开发利器.JSF 的开发环境搭建涉及到在 ...

  2. postgresql 10 数据类型 (完整版)

    官方数据类型document https://www.postgresql.org/docs/10/static/datatype.html PostgreSQL拥有丰富的数据类型供用户使用.用户也可 ...

  3. Building MFC application with /MD[d] (CRT dll version)requires MFC shared dll version

    解决方法:

  4. [TJOI2014] Alice and Bob

    非常好的一道思维性题目,想了很久才想出来qwq(我好笨啊) 考虑a[]数组有什么用,首先可以yy出一些性质 (设num[i]为原来第i个位置的数是什么 , 因为题目说至少有一个排列可以满足a[],所以 ...

  5. CoreData: 如何预载/导入已有的数据

    原文地址:CoreData: 如何预载/导入已有的数据作者:出其东门 在系列教程一中,我们为对象建立了可视化数据模型,运行了快速肮脏测试并勾在一个表视图(table view)中来显示.而在这个教程, ...

  6. 检查iOS app 是否升级为新版本

    之前我帮某公司做的一个iOS app,升级的时候发现闪退问题.后来检查是因为升级的时候数据库出现一点小问题导致对象为空. 下面这个代码可以检测程序是否更新了,从而进行相关处理: 1 2 3 4 5 6 ...

  7. 【spring cloud】注解@SpringCloudApplication和@SpringBootApplication的区别

    @SpringCloudApplication和@SpringBootApplication的区别

  8. 模拟登陆web微信的流程和参数细节

    这几天在用python写了一个模拟登陆web微信,发送和接受信息的程序.发现步骤不多,但需要的参数太多了 整个过程中,务必保证session.headers.cookie一致,不然的话,中间会出现登陆 ...

  9. HDU1421

    提交啦n次一直WA,这个bug找啦几个小时,最终才发现数组开小啦,真是遗憾.这是一个典型的DP问题,题目要求从n个中选出k对使得最终疲劳度最小.首先对物品质量a[n]进行一次排序,用dp[i][j]表 ...

  10. 左手系,右手系,row major, column major

    http://www.cnblogs.com/minggoddess/p/3672863.html dx 左手系 row major ogl 右手系 column major 差了个 matrix   ...