Volley与WebView共享Cookie

现在很多APP开发都采用native + web的方式,这样就会遇到需要共享Cookie的情况。试想用户已经在app里面登陆过了,不可能进了web页面让用户再次登录,这样肯定不合理。

一般的做法是在app里面收到含有Set-Cookie(Set-Cookie2)的响应时,手动取出这个head,保存在ram或者disk里面,之后发起请求时在Webview里面拦截url,然后把之前保存的Cookie取出来,塞到这个请求的headers里面去。这种方法可行,但是略显麻烦,每次发起请求都要手动去添加Cookie,这里我要说的是另外一种。

java里面自带有管理Cookie的工具java.net.CookieManager,它可以管理所有通过HttpUrlConnection(HttpClient没有试过)发起的会话中的Cookie,只要我们初始化之后,它就会自动的保存Cookie,然后在通过HttpUrlConnection发起请求的时候会自动将之前保存的Cookie添加到请求的headers里面去,不用我们手动来做保存和添加Cookies的操作。

其初始化方法:

//这是最简单的写法
CookieHandler.setDefault(new java.net.CookieManager());

而Webview也有一个管理Cookie的工具类android.webkit.CookieManager,我们也可以简单地设置后就能达到自动保存和添加Cookie的效果,但是它管理的是webview里面的会话,无法管理HttpUrlConnection会话的Cookie。

这里我们需要做的操作就是让两个CookieManager里面的内容保持一致。

由于我们一般需要共享Cookie大多都是登录状态,而这个登录发生在App内部(通过web登陆,并将Cookie共享到Native的情况还没遇到过,这里没有考虑这种情况),故而我们只需要在java.net.CookieManager进行SetCookie操作的时候把这个Cookie set到android.webkit.CookieManager就行了。

下一步要做的操作就是找到java.net.CookieManager是在哪里进行的set操作,通过其源代码我们可以很清楚的看到java.net.CookieManager在管理Cookie时的核心是java.net.CookieStore这个接口,java里面已经有一个默认的实现CookieStoreImpl,CookieStoreImpl里面只是用了一个HashMap来保存Cookie,整个类非常简单。通过上面的分析我们知道,只要在add方法里面将Cookie同时set到android.webkit.CookieManager里面就行了。下面上代码

//这个类是直接将CookieStoreImpl这个类的内容copy了出来,然后加入了android.webkit.CookieManager这个变量,并在add操作时,将Cookie添加到android.webkit.CookieManager中去,这样就实现了Cookie的共享,如果你要实现将Cookie保存到本地,可以自己实现CookieStore接口,只要记住在add方法里面将Cookie set到android.webkit.CookieManager里面就行了
class CookieStore_ implements CookieStore{
    /** this map may have null keys! */
    private final Map<URI, List<HttpCookie>> map = new HashMap<URI, List<HttpCookie>>();
    private android.webkit.CookieManager manager;

    public CookieStore_() {
        manager = android.webkit.CookieManager.getInstance();
    }

    public synchronized void add(URI uri, HttpCookie cookie) {
        if (cookie == null) {
            throw new NullPointerException("cookie == null");
        }

        uri = cookiesUri(uri);
        manager.setCookie(uri.toString(),cookie.toString());

        List<HttpCookie> cookies = map.get(uri);
        if (cookies == null) {
            cookies = new ArrayList<HttpCookie>();
            map.put(uri, cookies);
        } else {
            logger.printS("remove old one -> " + cookie.toString());
            cookies.remove(cookie);
        }
        cookies.add(cookie);

        logger.printS("current size -> " + map.get(uri).size());
    }

    private URI cookiesUri(URI uri) {
        if (uri == null) {
            return null;
        }
        try {
            return new URI("http", uri.getHost(), null, null);
        } catch (URISyntaxException e) {
            return uri; // probably a URI with no host
        }
    }

    public synchronized List<HttpCookie> get(URI uri) {
        if (uri == null) {
            throw new NullPointerException("uri == null");
        }

        List<HttpCookie> result = new ArrayList<HttpCookie>();

        // get cookies associated with given URI. If none, returns an empty list
        List<HttpCookie> cookiesForUri = map.get(uri);
        if (cookiesForUri != null) {
            for (Iterator<HttpCookie> i = cookiesForUri.iterator(); i.hasNext(); ) {
                HttpCookie cookie = i.next();
                if (cookie.hasExpired()) {
                    i.remove(); // remove expired cookies
                } else {
                    result.add(cookie);
                }
            }
        }

        // get all cookies that domain matches the URI
        for (Map.Entry<URI, List<HttpCookie>> entry : map.entrySet()) {
            if (uri.equals(entry.getKey())) {
                continue; // skip the given URI; we've already handled it
            }

            List<HttpCookie> entryCookies = entry.getValue();
            for (Iterator<HttpCookie> i = entryCookies.iterator(); i.hasNext(); ) {
                HttpCookie cookie = i.next();
                if (!HttpCookie.domainMatches(cookie.getDomain(), uri.getHost())) {
                    continue;
                }
                if (cookie.hasExpired()) {
                    i.remove(); // remove expired cookies
                } else if (!result.contains(cookie)) {
                    result.add(cookie);
                }
            }
        }

        logger.printS(String.format(Locale.US,"get cookie for %s, result size is %d",uri.toString(),result.size()));

        return Collections.unmodifiableList(result);
    }

    public synchronized List<HttpCookie> getCookies() {
        List<HttpCookie> result = new ArrayList<HttpCookie>();
        for (List<HttpCookie> list : map.values()) {
            for (Iterator<HttpCookie> i = list.iterator(); i.hasNext(); ) {
                HttpCookie cookie = i.next();
                if (cookie.hasExpired()) {
                    i.remove(); // remove expired cookies
                } else if (!result.contains(cookie)) {
                    result.add(cookie);
                }
            }
        }
        return Collections.unmodifiableList(result);
    }

    public synchronized List<URI> getURIs() {
        List<URI> result = new ArrayList<URI>(map.keySet());
        result.remove(null); // sigh
        return Collections.unmodifiableList(result);
    }

    public synchronized boolean remove(URI uri, HttpCookie cookie) {
        if (cookie == null) {
            throw new NullPointerException("cookie == null");
        }

        List<HttpCookie> cookies = map.get(cookiesUri(uri));
        if (cookies != null) {
            return cookies.remove(cookie);
        } else {
            return false;
        }
    }

    public synchronized boolean removeAll() {
        boolean result = !map.isEmpty();
        map.clear();
        return result;
    }

    public void clearCookies(){
        map.clear();
    }
}
//CookieStore_的用法,只需要在发起网络操作之前调用就行,求简便的话可以放在Application的onCreate函数里面
CookieHandler.setDefault(new java.net.CookieManager(cookieStore_, CookiePolicy.ACCEPT_ORIGINAL_SERVER));

android的webview管理Cookie在Android lollipop之前和之后是不一样的,在Android L之前CookieManager需要和CookieSyncManager这个类一起使用,不然在调用CookieManager的setCookie方法时程序会崩溃,但是在L以及之后的版本中这个类已经Deprecated了,有些API发生了变动,因此在写法上就有些不同了,下面是我自己定义的一个工具类。

//这是一个自定义的工具类,基本的操作都定义在里面了。
public class CookieUtil {
    private CookieManager manager;
    private CookieStore_ cookieStore_;
    private CookieSyncManager syncManager;
    private static CookieUtil cookieUtil;
    private boolean isInitialed = false;

    private CookieUtil(Context context) {
        manager = CookieManager.getInstance();

        if (!Util.hasLollipop()){
            syncManager = CookieSyncManager.createInstance(context);
        }
        cookieStore_ = new CookieStore_();
    }

    public void clearCookies(){
        if (manager.hasCookies()){
            if (Util.hasLollipop()){
                manager.removeAllCookies(null);
            }else {
                manager.removeAllCookie();
            }
        }
        cookieStore_.clearCookies();
    }

    public static CookieUtil getCookieUtil(Context context){
        if (cookieUtil == null)
            cookieUtil = new CookieUtil(context);
        return cookieUtil;
    }

    public void sync(){
        if (Util.hasLollipop()){
            manager.flush();
        }else {
            syncManager.sync();
        }
    }

    public void setThirdPartyCookieAcceptable(WebView webView){
        if (Util.hasLollipop()){
            manager.setAcceptThirdPartyCookies(webView,true);
        }
    }

    public void initCookieHandler(){
        if (isInitialed)
            return;
        isInitialed = true;
        CookieHandler.setDefault(new java.net.CookieManager(cookieStore_, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
    }
}

没有特殊需求的话,只需要一句话就可以完成所有的Cookie管理操作

CookieUtil.getCookieUtil(context).initCookieHandler();//只需要调用一次就够了

如果你的程序里面有退出登录的操作的话,不要忘记调用clearCookies()来清空缓存。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值