okhttp3 cookie 持久化 以及 webview 携带 cookie的方法

一、关于cookie的用处:

当app需要保持用户登陆状态等,这个时候我们通常就要进行对cookie的管理来实现。如果你使用的是okhttp网络请求,那么直接对cookie进行持久化管理即可。

二、okhttp3如何进行cookie的持久化处理:

在okhttp3中新增了Cookiejar这个借口,我们可以直接进行cookie的持久化管理,具体看一下代码:

public class OkManager {
    private OkHttpClient client;
    private volatile static OkManager okManager;
    private final String TAG = OkManager.class.getSimpleName();

    OkHttpClient.Builder httpBuilder;
   
    private List<Cookie> cookie = new ArrayList<>();
    private static final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
    private static List<Cookie> cookies;
    public static void cleanCookie(){
        cookieStore.clear();
    }

   public static String getCookie(){
       for (int i = 0; i<cookies.size();i++){
           return  cookies.get(i).toString();
      }
       return null;
    }
    public OkManager() {
        handler = new Handler(Looper.getMainLooper());
        mGson = new Gson();
        //不需要设置请求超时是调用
//        client = new OkHttpClient();
        //需要设置请求超时调用下面两行
        httpBuilder=new OkHttpClient.Builder();
        client=httpBuilder.readTimeout(10, TimeUnit.SECONDS)
                .connectTimeout(10, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS) //设置超时
                .cookieJar(new CookieJar() {
                   @Override
                   public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
                        cookieStore.put(httpUrl.host(), list);
                    }

                   @Override
                    public List<Cookie> loadForRequest(HttpUrl httpUrl) {
                        cookies = cookieStore.get(httpUrl.host());
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                .cookieJar(new CookieJarImpl(new SPCookieStore(CashLoanApp.getInstance())))
                .build();
    }
    /**
     * 采用单例获取对象
     *
     * @return
     */
    public static OkManager getInstance() {
        if (okManager == null) {
            synchronized (OkManager.class) {
                if (okManager == null) {
                    okManager = new OkManager();
                }
            }
        }
        return okManager;
    }

这种做法可以简单做到cookie的持久化管理,但是当app杀死进程等一些情况下会造成cookie持久化的失效。

所以,我们要自己实现cookieJar进行cookie的存储,参考OKgo的做法,此次用sharedPreferences 对cookie进行管理。具体代码如下:

1、首先是cookieJar的实现类:

public class CookieJarImpl implements CookieJar {

    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<Cookie> cookies) {
        cookieStore.saveCookie(url, cookies);
    }

    @Override
    public synchronized List<Cookie> loadForRequest(HttpUrl url) {
        return cookieStore.loadCookie(url);
    }

    public CookieStore getCookieStore() {
        return cookieStore;
    }
}

2、我们再提供一个管理cookie的公共接口:

public interface CookieStore {

    /** 保存url对应所有cookie */
    void saveCookie(HttpUrl url, List<Cookie> cookie);

    /** 保存url对应所有cookie */
    void saveCookie(HttpUrl url, Cookie cookie);

    /** 加载url所有的cookie */
    List<Cookie> loadCookie(HttpUrl url);

    /** 获取当前所有保存的cookie */
    List<Cookie> getAllCookie();

    /** 获取当前url对应的所有的cookie */
    List<Cookie> getCookie(HttpUrl url);

    /** 根据url和cookie移除对应的cookie */
    boolean removeCookie(HttpUrl url, Cookie cookie);

    /** 根据url移除所有的cookie */
    boolean removeCookie(HttpUrl url);

    /** 移除所有的cookie */
    boolean removeAllCookie();
}

3、进行序列化的实现:

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

4、使用 SharedPreferences 持久化存储 cookie:

public class SPCookieStore implements CookieStore {

    private static final String COOKIE_PREFS = "sp_cookie";           //cookie使用prefs保存
    private static final String COOKIE_NAME_PREFIX = "cookie_";         //cookie持久化的统一前缀


    private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
    private final SharedPreferences cookiePrefs;

    public SPCookieStore(Context context) {
        cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);
        cookies = new HashMap<>();

        //将持久化的cookies缓存到内存中,数据结构为 Map<Url.host, Map<CookieToken, Cookie>>
        Map<String, ?> prefsMap = cookiePrefs.getAll();
        for (Map.Entry<String, ?> 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<String, Cookie>());
                            }
                            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<Cookie> 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<String, Cookie>());
        }
        //当前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<Cookie> loadCookie(HttpUrl url) {
        List<Cookie> ret = new ArrayList<>();
        if (!cookies.containsKey(url.host())) return ret;

        Collection<Cookie> 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<String, Cookie> urlCookie = cookies.remove(url.host());
        //文件移除
        Set<String> 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<Cookie> getAllCookie() {
        List<Cookie> ret = new ArrayList<>();
        for (String key : cookies.keySet()) {
            ret.addAll(cookies.get(key).values());
        }
        return ret;
    }

    @Override
    public synchronized List<Cookie> getCookie(HttpUrl url) {
        List<Cookie> ret = new ArrayList<>();
        Map<String, Cookie> mapCookie = cookies.get(url.host());
        if (mapCookie != null) ret.addAll(mapCookie.values());
        return ret;
    }
}

5、进行取出cookie和删除cookie等公共类的封装:

/**
 * cookie管理工具类
 */
public class CookieUtil {
    /**
     * 获取指定URL对应的cookie
     * @param baseUrl
     * @param url
     * @return
     */
    public static List<Cookie> cookies(String baseUrl , String url){
        //一般手动取出cookie的目的只是交给 webview 等等,非必要情况不要自己操作
        CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
        HttpUrl httpUrl = HttpUrl.parse(baseUrl + url);
        List<Cookie> cookies = cookieStore.getCookie(httpUrl);
        Log.e( "cookies: ",httpUrl.host() + "对应的cookie如下:" + cookies.toString() );
        return cookies;
    }

    /**
     * 获取所有的cookie
     * @return
     */
    public static List<Cookie> cookieList(){
        CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
        List<Cookie> allCookie = cookieStore.getAllCookie();
        Log.e( "所有cookie如下: ",allCookie.toString() );
        return allCookie;
    }
    public static String getCookie(){
        CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
        List<Cookie> allCookie = cookieStore.getAllCookie();
        for (int i = 0; i<allCookie.size();i++){
           return  allCookie.get(i).toString();
        }
       return null;
    }
    /**
     * 删除cookie(这里是全部删除,也可指定的地址删除)
     */
    public static void removeCookie(){
//        HttpUrl httpUrl = HttpUrl.parse(Urls.URL_METHOD);
        CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
//        cookieStore.removeCookie(httpUrl);
        cookieStore.removeAllCookie();

    }
}

最后在用自己实现的CookieJarImpl代替cookieJar

public class OkManager {
    private OkHttpClient client;
    private volatile static OkManager okManager;
    private final String TAG = OkManager.class.getSimpleName();

    OkHttpClient.Builder httpBuilder;
    
    public OkManager() {
        handler = new Handler(Looper.getMainLooper());
        mGson = new Gson();
        //不需要设置请求超时是调用

        //需要设置请求超时调用下面两行
        httpBuilder=new OkHttpClient.Builder();
        client=httpBuilder.readTimeout(10, TimeUnit.SECONDS)
                .connectTimeout(10, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS) //设置超时
                .cookieJar(new CookieJarImpl(new SPCookieStore(CashLoanApp.getInstance())))
                .build();
    }
    /** 获取全局的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;
    }

这样就完成了cookie的持久化操作。


三、如果需要把cookie交给webview可以这样操作 :
 /**
     * 把cookie交给webview
     *
     * @param context
     * @param url
     * @param cookies
     */
    public static void synchronousWebCookies(Context context, String url, String cookies) {
        if (!TextUtils.isEmpty(url))
            if (!TextUtils.isEmpty(cookies)) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    CookieSyncManager.createInstance(context);
                }
                CookieManager cookieManager = CookieManager.getInstance();
                cookieManager.setAcceptCookie(true);
                cookieManager.removeSessionCookie();// 移除
                cookieManager.removeAllCookie();
                StringBuilder sbCookie = new StringBuilder();
                sbCookie.append(cookies);
                String cookieValue = sbCookie.toString();
                Log.e("synchronousWebCookies: ", cookieValue);
                cookieManager.setCookie(url, cookieValue);//为url设置cookie

                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    CookieSyncManager.getInstance().sync();
                } else {
                    cookieManager.flush();
                }

           
            }
    }

在webView.loadUrl(url);方法前进行以下操作:

 String cookie = CookieUtil.getCookie();
        synchronousWebCookies(this, url, cookie);
这样就可以完成webview携带cookie。




  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值