当app需要保持用户登陆状态等,这个时候我们通常就要进行对cookie的管理来实现。如果你使用的是okhttp网络请求,那么就可以直接对cookie进行持久化管理。
主要原理是使用okhttp3中新增的Cookiejar这个接口,通过这个接口我们可以直接进行cookie的持久化管理,代码如下:
首先实现cookieJar,里面有一个管理cookie的公共接口CookieStore,代码如下:
import java.net.CookieStore;
import java.util.List;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
/**
* @author dmrfcoder
* @date 2018/8/28
*/
public class CookieJarImpl implements CookieJar {
public interface CookieStore {
/**
* 保存url对应所有cookie
*/
void saveCookie(HttpUrl url, List cookie);
/**
* 保存url对应所有cookie
*/
void saveCookie(HttpUrl url, Cookie cookie);
/**
* 加载url所有的cookie
*/
List loadCookie(HttpUrl url);
/**
* 获取当前所有保存的cookie
*/
List getAllCookie();
/**
* 获取当前url对应的所有的cookie
*/
List getCookie(HttpUrl url);
/**
* 根据url和cookie移除对应的cookie
*/
boolean removeCookie(HttpUrl url, Cookie cookie);
/**
* 根据url移除所有的cookie
*/
boolean removeCookie(HttpUrl url);
/**
* 移除所有的cookie
*/
boolean removeAllCookie();
}
private CookieStore cookieStore;
public CookieJarImpl(CookieStore cookieStore) {
if (cookieStore == null) {
throw new IllegalArgumentException("cookieStore can not be null!");
}
this.cookieStore = cookieStore;
}
@Override
public synchronized void saveFromResponse(HttpUrl url, List cookies) {
cookieStore.saveCookie(url, cookies);
}
@Override
public synchronized List loadForRequest(HttpUrl url) {
return cookieStore.loadCookie(url);
}
public CookieStore getCookieStore() {
return cookieStore;
}
}
实现cookie的序列化:
import android.content.ContentValues;
import android.database.Cursor;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Locale;
import okhttp3.Cookie;
/**
* @author dmrfcoder
* @date 2018/8/28
*/
public class SerializableCookie implements Serializable {
private static final long serialVersionUID = 6374381323722046732L;
public static final String HOST = "host";
public static final String NAME = "name";
public static final String DOMAIN = "domain";
public static final String COOKIE = "cookie";
public String host;
public String name;
public String domain;
private transient Cookie cookie;
private transient Cookie clientCookie;
public SerializableCookie(String host, Cookie cookie) {
this.cookie = cookie;
this.host = host;
this.name = cookie.name();
this.domain = cookie.domain();
}
public Cookie getCookie() {
Cookie bestCookie = cookie;
if (clientCookie != null) {
bestCookie = clientCookie;
}
return bestCookie;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(cookie.name());
out.writeObject(cookie.value());
out.writeLong(cookie.expiresAt());
out.writeObject(cookie.domain());
out.writeObject(cookie.path());
out.writeBoolean(cookie.secure());
out.writeBoolean(cookie.httpOnly());
out.writeBoolean(cookie.hostOnly());
out.writeBoolean(cookie.persistent());
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
String name = (String) in.readObject();
String value = (String) in.readObject();
long expiresAt = in.readLong();
String domain = (String) in.readObject();
String path = (String) in.readObject();
boolean secure = in.readBoolean();
boolean httpOnly = in.readBoolean();
boolean hostOnly = in.readBoolean();
boolean persistent = in.readBoolean();
Cookie.Builder builder = new Cookie.Builder();
builder = builder.name(name);
builder = builder.value(value);
builder = builder.expiresAt(expiresAt);
builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
builder = builder.path(path);
builder = secure ? builder.secure() : builder;
builder = httpOnly ? builder.httpOnly() : builder;
clientCookie = builder.build();
}
public static SerializableCookie parseCursorToBean(Cursor cursor) {
String host = cursor.getString(cursor.getColumnIndex(HOST));
byte[] cookieBytes = cursor.getBlob(cursor.getColumnIndex(COOKIE));
Cookie cookie = bytesToCookie(cookieBytes);
return new SerializableCookie(host, cookie);
}
public static ContentValues getContentValues(SerializableCookie serializableCookie) {
ContentValues values = new ContentValues();
values.put(SerializableCookie.HOST, serializableCookie.host);
values.put(SerializableCookie.NAME, serializableCookie.name);
values.put(SerializableCookie.DOMAIN, serializableCookie.domain);
values.put(SerializableCookie.COOKIE, cookieToBytes(serializableCookie.host, serializableCookie.getCookie()));
return values;
}
/**
* cookies 序列化成 string
*
* @param cookie 要序列化
* @return 序列化之后的string
*/
public static String encodeCookie(String host, Cookie cookie) {
if (cookie == null) {
return null;
}
byte[] cookieBytes = cookieToBytes(host, cookie);
return byteArrayToHexString(cookieBytes);
}
public static byte[] cookieToBytes(String host, Cookie cookie) {
SerializableCookie serializableCookie = new SerializableCookie(host, cookie);
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(serializableCookie);
} catch (IOException e) {
Log.e("cookieToBytes: ", e.toString());
return null;
}
return os.toByteArray();
}
/**
* 将字符串反序列化成cookies
*
* @param cookieString cookies string
* @return cookie object
*/
public static Cookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);
return bytesToCookie(bytes);
}
public static Cookie bytesToCookie(byte[] bytes) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Cookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
cookie = ((SerializableCookie) objectInputStream.readObject()).getCookie();
} catch (Exception e) {
Log.e("bytesToCookie: ", e.toString());
}
return cookie;
}
/**
* 二进制数组转十六进制字符串
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
private static 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);
}
/**
* 十六进制字符串转二进制数组
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
private static 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;
}
/**
* host, name, domain 标识一个cookie是否唯一
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SerializableCookie that = (SerializableCookie) o;
if (host != null ? !host.equals(that.host) : that.host != null) {
return false;
}
return (name != null ? name.equals(that.name) : that.name == null) && (domain != null ? domain.equals(that.domain) : that.domain == null);
}
@Override
public int hashCode() {
int result = host != null ? host.hashCode() : 0;
result = 31 * result (name != null ? name.hashCode() : 0);
result = 31 * result (domain != null ? domain.hashCode() : 0);
return result;
}
}
使用 SharedPreferences 持久化存储 cookie:
package cn.xiaojii.cashgift.bean.net;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
/**
* @author dmrfcoder
* @date 2018/8/28
*/
public class SPCookieStore implements CookieJarImpl.CookieStore {
private static final String COOKIE_PREFS = "sp_cookie"; //cookie使用prefs保存
private static final String COOKIE_NAME_PREFIX = "cookie_"; //cookie持久化的统一前缀
private final Map> cookies;
private final SharedPreferences cookiePrefs;
public SPCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);
cookies = new HashMap<>();
//将持久化的cookies缓存到内存中,数据结构为 Map>
Map prefsMap = cookiePrefs.getAll();
for (Map.Entry entry : prefsMap.entrySet()) {
if ((entry.getValue()) != null && !entry.getKey().startsWith(COOKIE_NAME_PREFIX)) {
//获取url对应的所有cookie的key,用","分割
String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
for (String name : cookieNames) {
//根据对应cookie的Key,从xml中获取cookie的真实值
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX name, null);
if (encodedCookie != null) {
Cookie decodedCookie = SerializableCookie.decodeCookie(encodedCookie);
if (decodedCookie != null) {
if (!cookies.containsKey(entry.getKey())) {
cookies.put(entry.getKey(), new ConcurrentHashMap());
}
cookies.get(entry.getKey()).put(name, decodedCookie);
}
}
}
}
}
}
private String getCookieToken(Cookie cookie) {
return cookie.name() "@" cookie.domain();
}
/**
* 当前cookie是否过期
*/
private static boolean isCookieExpired(Cookie cookie) {
return cookie.expiresAt() < System.currentTimeMillis();
}
/**
* 将url的所有Cookie保存在本地
*/
@Override
public synchronized void saveCookie(HttpUrl url, List urlCookies) {
for (Cookie cookie : urlCookies) {
saveCookie(url, cookie);
}
}
@Override
public synchronized void saveCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) {
cookies.put(url.host(), new ConcurrentHashMap());
}
//当前cookie是否过期
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
saveCookie(url, cookie, getCookieToken(cookie));
}
}
/**
* 保存cookie,并将cookies持久化到本地
*/
private void saveCookie(HttpUrl url, Cookie cookie, String cookieToken) {
//内存缓存
cookies.get(url.host()).put(cookieToken, cookie);
//文件缓存
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX cookieToken, SerializableCookie.encodeCookie(url.host(), cookie));
prefsWriter.apply();
}
/**
* 根据当前url获取所有需要的cookie,只返回没有过期的cookie
*/
@Override
public synchronized List loadCookie(HttpUrl url) {
List ret = new ArrayList<>();
if (!cookies.containsKey(url.host())) {
return ret;
}
Collection urlCookies = cookies.get(url.host()).values();
for (Cookie cookie : urlCookies) {
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
ret.add(cookie);
}
}
return ret;
}
/**
* 根据url移除当前的cookie
*/
@Override
public synchronized boolean removeCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) {
return false;
}
String cookieToken = getCookieToken(cookie);
if (!cookies.get(url.host()).containsKey(cookieToken)) {
return false;
}
//内存移除
cookies.get(url.host()).remove(cookieToken);
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
if (cookiePrefs.contains(COOKIE_NAME_PREFIX cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX cookieToken);
}
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeCookie(HttpUrl url) {
if (!cookies.containsKey(url.host())) {
return false;
}
//内存移除
ConcurrentHashMap urlCookie = cookies.remove(url.host());
//文件移除
Set cookieTokens = urlCookie.keySet();
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for (String cookieToken : cookieTokens) {
if (cookiePrefs.contains(COOKIE_NAME_PREFIX cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX cookieToken);
}
}
prefsWriter.remove(url.host());
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeAllCookie() {
//内存移除
cookies.clear();
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.apply();
return true;
}
/**
* 获取所有的cookie
*/
@Override
public synchronized List getAllCookie() {
List ret = new ArrayList<>();
for (String key : cookies.keySet()) {
ret.addAll(cookies.get(key).values());
}
return ret;
}
@Override
public synchronized List getCookie(HttpUrl url) {
List ret = new ArrayList<>();
Map mapCookie = cookies.get(url.host());
if (mapCookie != null) {
ret.addAll(mapCookie.values());
}
return ret;
}
}
进行取出cookie和删除cookie等公共类的封装:
/**
* @author dmrfcoder
* @date 2018/8/28
*/
import android.util.Log;
import java.util.List;
import cn.xiaojii.cashgift.bean.net.CookieJarImpl;
import cn.xiaojii.cashgift.bean.net.OkManager;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
/**
* cookie管理工具类
* @author dmrfcoder
*/
public class CookieUtil {
/**
* 获取指定URL对应的cookie
*
* @param baseUrl
* @param url
* @return
*/
public static List cookies(String baseUrl, String url) {
//一般手动取出cookie的目的只是交给 webview 等等,非必要情况不要自己操作
CookieJarImpl.CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
HttpUrl httpUrl = HttpUrl.parse(baseUrl url);
List cookies = cookieStore.getCookie(httpUrl);
Log.e("cookies: ", httpUrl.host() "对应的cookie如下:" cookies.toString());
return cookies;
}
/**
* 获取所有的cookie
*
* @return
*/
public static List cookieList() {
CookieJarImpl.CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
List allCookie = cookieStore.getAllCookie();
Log.e("所有cookie如下: ", allCookie.toString());
return allCookie;
}
public static String getCookie() {
CookieJarImpl.CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
List allCookie = cookieStore.getAllCookie();
for (int i = 0; i < allCookie.size(); i ) {
return allCookie.get(i).toString();
}
return null;
}
/**
* 删除cookie(这里是全部删除,也可指定的地址删除)
*/
public static void removeCookie() {
CookieJarImpl.CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
cookieStore.removeAllCookie();
}
}
最终的Okmanager类:
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
/**
* @author dmrfcoder
* @date 2018/8/28
*/
public class OkManager {
public OkHttpClient getClient() {
return client;
}
private OkHttpClient client;
private volatile static OkManager okManager;
private final String TAG = OkManager.class.getSimpleName();
OkHttpClient.Builder httpBuilder;
public OkManager() {
//需要设置请求超时调用下面两行
httpBuilder = new OkHttpClient.Builder();
client = httpBuilder.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS) //设置超时
.cookieJar(new CookieJarImpl(new SPCookieStore(CashApplication.getContextObject())))
.build();
//将上面的CashApplication换成你自己的Application或者传入同样性质的参数
}
/**
* 获取全局的cookie实例
*/
public CookieJarImpl getCookieJar() {
return (CookieJarImpl) client.cookieJar();
}
/**
* 采用单例获取对象
*
* @return
*/
public static OkManager getInstance() {
if (okManager == null) {
synchronized (OkManager.class) {
if (okManager == null) {
okManager = new OkManager();
}
}
}
return okManager;
}
}
private OkHttpClient okHttpClient = OkManager.getInstance().getClient();
然后就实现了cookie的持久化。