Android中在使用OkHttp这个库的时候,有时候需要持久化Cookie,那么怎么实现呢。OkHttp的内部源码过于复杂,不进行深究,这里只看其中的HttpEngineer里面的部分源码,在发起请求以及请求结束都会调用这个类的几个方法。我们先看networkRequest方法,在里面通过client.getCookieHandler()函数获得了CookieHandler对象,通过该对象拿到cookie并设置到请求头里,请求结束后取得响应后通过networkResponse.headers()函数将请求头获得传入receiveHeaders函数,并将取得的cookie存入getCookieHandler得到的一个CookieHandler对象中去

/** * Populates request with defaults and cookies. * * 

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

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的自动管理功能,主要我们参考其中的两个类

  • PersistentCookieStore
  • SerializableCookie

参考以上两个类并做适当修改,得到了如下两个类,他们的功能就是将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 <-> 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; } }" data-snippet-id="ext.8d16b4a8fcca2def684a77bb76fa9bc7" data-snippet-saved="false" data-csrftoken="JJvR2MgM-MBb6XEHHuKsz9WziKXBKYfoLLYQ" data-codota-status="done">                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> 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 <-> 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了。

更多相关文章

  1. 图解 Android(安卓)Handler 线程消息机制
  2. 图解 Android(安卓)Handler 线程消息机制
  3. Android调用WebService系列之KSoap2对象解析
  4. Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程
  5. Android调用WebService系列之KSoap2对象解析
  6. Android天气预报详解
  7. Android的内存泄漏和调试
  8. Android实现电池管理系统
  9. Android中网络编程以及与服务器上Web项目的基础交互

随机推荐

  1. Android主进程判断
  2. Android模拟器无法启动
  3. Android中获取网页表单中的数据
  4. Android(安卓)标签 (FlexboxLayout实现标
  5. Android(安卓)创建文件电脑端不显示
  6. Android(安卓)自定义ProgressDialog
  7. android 1.5~2.2源码下载
  8. Android(安卓)USB转 串口
  9. rk3288 7.1去掉桌面搜索框
  10. Android(安卓)通过 IMSI 判断手机运营商