OkHttp3 cookie 本地SharedPrefrence管理

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()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容