interface ICookieStore {
/**保存对应url 的cookie*/
fun saveCookie(url: HttpUrl?, cookies: MutableList<Cookie>?)
/**获取对应url的所有cookie*/
fun loadCookie(httpUrl: HttpUrl): List<Cookie>
/**加载所有cookie*/
fun loadAllCookie(): List<Cookie>
/**移除多有cookie*/
fun removeAllCookie(): Boolean
/**移除对应url的cookie*/
fun removeCookie(httpUrl: HttpUrl): Boolean
}
/**
* Created by HeXiangyuan on 18-3-21.
* Description:这个cookie进行本地化保存 保存在SP opt_cookie_sp中
*
* sp中储存方式 是 key :url.host value :,opr_cookie_cookie1,opt_cookie_cookie2,opt_cookie_cookie3
*/
object CookieMgr {
const val COOKIE_SP = "OPT_COOKIE_SP"
private const val COOKIE_PREFIX = ",opt_cookie_"
fun mapCookiesToString(httpUrl: HttpUrl, cookies: List<Cookie>): String {
return if (cookies.isEmpty()) {
""
} else {
val sb = StringBuilder()
cookies.forEach {
sb.append(CookieMgr.COOKIE_PREFIX)
sb.append(SerializableCookie.encodeCookie(httpUrl.host(), it))
}
sb.toString()
}
}
fun mapStringToCookies(cookieString: String): MutableList<Cookie> {
return if (cookieString.isEmpty()) {
mutableListOf()
} else {
val cookies = arrayListOf<Cookie>()
cookieString
.split(CookieMgr.COOKIE_PREFIX)
.forEach {
if (!it.isEmpty()) {
val cookie = SerializableCookie.decodeCookie(it)
Log.e("cookieLog", "name == ${cookie.name()} domain == ${cookie.domain()} cookieString == $cookie ")
cookies.add(cookie)
}
}
return cookies
}
}
/**以Cookie的name 和domain 作为判断依据,
* 如果是 一致的则通过map 的key 唯一性进行替换
* 也就是说如果两个cookie 对比name 和domain
* */
fun addToCookies(originCookies: MutableList<Cookie>, cookies: MutableList<Cookie>?): MutableList<Cookie> {
if (cookies == null || cookies.size == 0) {
return originCookies
} else {
val cookieMap = ConcurrentHashMap<String, Cookie>()
originCookies.forEach {
cookieMap.put("${it.name()}@${it.domain()}", it)
}
cookies.forEach {
cookieMap.put("${it.name()}@${it.domain()}", it)
}
val currentCookie = mutableListOf<Cookie>()
cookieMap.forEach {
currentCookie.add(it.value)
}
return currentCookie
}
}
}
class LocalCookieStore(val context: Application) : ICookieStore {
private val cookieSp: SharedPreferences = context.getSharedPreferences(CookieMgr.COOKIE_SP, MODE_PRIVATE)
private val cookiesMap: ConcurrentHashMap<String, MutableList<Cookie>> = ConcurrentHashMap()
init {
//初始化之前先将Cookie全部从Sp中读取到内存中
cookieSp.all.forEach {
if (it.value != null) {
cookiesMap[it.key] = CookieMgr.mapStringToCookies(it.value.toString())
}
}
}
@Synchronized
override fun saveCookie(url: HttpUrl?, cookies: MutableList<Cookie>?) {
if (cookies != null && url != null) {
//先存到内存在缓存
val newCookie = CookieMgr.addToCookies(cookiesMap[url.host()] ?: arrayListOf(), cookies)
cookiesMap[url.host()] = newCookie
val prefsWriter = cookieSp.edit()
prefsWriter.putString(url.host(), CookieMgr.mapCookiesToString(url, newCookie))
prefsWriter.apply()
}
}
override fun loadCookie(httpUrl: HttpUrl): List<Cookie> {
if (cookiesMap.containsKey(httpUrl.host())) {
return cookiesMap[httpUrl.host()] ?: emptyList()
}
return emptyList()
}
override fun loadAllCookie(): List<Cookie> {
val cookies = arrayListOf<Cookie>()
cookiesMap.forEach {
it.value.forEach { cookie ->
cookies.add(cookie)
}
}
return cookies
}
override fun removeAllCookie(): Boolean {
cookiesMap.clear()
cookieSp.edit().clear().apply()
return true
}
override fun removeCookie(httpUrl: HttpUrl): Boolean {
cookiesMap.remove(httpUrl.host())
cookieSp.edit().remove(httpUrl.host()).apply()
return true
}
}
class MyCookieJarImp( val cookieStore: ICookieStore) : CookieJar {
override fun saveFromResponse(url: HttpUrl?, cookies: MutableList<Cookie>?) {
cookieStore.saveCookie(url, cookies)
}
override fun loadForRequest(url: HttpUrl?): List<Cookie> {
return if (url != null) {
cookieStore.loadCookie(url)
} else {
emptyList()
}
}
}
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) {
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) {
}
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;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return 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;
}
}
OkHttpClient.Builder()
...
.cookieJar(MyCookieJarImp(LocalCookieStore(context)))
.build()