CookieManager setCookie Expires字

作者: Android架构 | 来源:发表于2019-02-28 21:09 被阅读2次

    英文状态下CookieManager setCookie Expires字段的识别

    CookieManager cookieManager = CookieManager.getInstance();
    String domain = ".bilibili.com";
    String cookie1 = "bili_jct=b33fd713ea7cc6d29d65462794b28441; Domain=.bilibili.com; Expires=Expires=Sun, 18 Jan 1970 22:11:45 GMT+08:00";
    String cookie2 = "bili_jct=49b9bcc2c21f19cc9dcb8382b84cf552; Domain=.bilibili.com; Expires=Expires=周日, 18 1月 1970 22:11:40 GMT+08:00";
    cookieManager.setCookie(domain, cookie1);
    String cookie1R = cookieManager.getCookie(domain);
    Log.i("Cookie","load cookie1 "+cookie1R);
    cookieManager.setCookie(domain, cookie2);
    String cookie2R = cookieManager.getCookie(domain);
    Log.i("Cookie","load cookie1 "+cookie2R);
    

    输出

    : load cookie1 null
    : load cookie1 bili_jct=49b9bcc2c21f19cc9dcb8382b84cf552
    

    问题原因就是英文状态是会识别Expires,所以在存入cookie1后CookieManager会认为这个cookie是过期的,所以会忽略掉这个cookie,返回null,但是在中文是不会识别Expires,所以可以返回cookie.

    我尝试去跟踪了一下源码去发现为什么会出现这种情况
    public abstract class CookieManager{
           public abstract void setCookie(String url, String value);
    }
    

    CookieManager是一个抽象类,setCookie也是一个抽象方法,所以必需找到其实现类

    public static CookieManager getInstance() {
            return WebViewFactory.getProvider().getCookieManager();
    }
    

    进入getProvider

    static WebViewFactoryProvider getProvider() {
            synchronized (sProviderLock) {
                // For now the main purpose of this function (and the factory abstraction) is to keep
                // us honest and minimize usage of WebView internals when binding the proxy.
                if (sProviderInstance != null) return sProviderInstance;
                //代码省略
                try {
                    Class<WebViewFactoryProvider> providerClass = getProviderClass();
                    Method staticFactory = null;
                    try {
                        staticFactory = providerClass.getMethod(
                            CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
                    } catch (Exception e) {
                        if (DEBUG) {
                            Log.w(LOGTAG, "error instantiating provider with static factory method", e);
                        }
                    }
    
                    Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
                    try {
                        sProviderInstance = (WebViewFactoryProvider)
                                staticFactory.invoke(null, new WebViewDelegate());
                        if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
                        return sProviderInstance;
                    } catch (Exception e) {
                        Log.e(LOGTAG, "error instantiating provider", e);
                        throw new AndroidRuntimeException(e);
                    } finally {
                        Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                    }
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                    StrictMode.setThreadPolicy(oldPolicy);
                }
            }
        }
    

    可以看到sProviderInstance就是我们要的WebViewFactoryProvider,WebViewFactoryProvider是一个接口,它的创建实际上分为两步。

    第一步:获取工厂类,这个工厂是用来创建WebViewFactoryProvider的实现类.

     Class<WebViewFactoryProvider> providerClass = getProviderClass();
    
    private static Class<WebViewFactoryProvider> getProviderClass() {
            //代码省略
           ClassLoader clazzLoader = webViewContext.getClassLoader();
           return getWebViewProviderClass(clazzLoader);
    }
    
    public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader)
                throws ClassNotFoundException {
            return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
                    true, clazzLoader);
        }
    
    private static final String CHROMIUM_WEBVIEW_FACTORY =
                "com.android.webview.chromium.WebViewChromiumFactoryProviderForO";
    

    WebViewChromiumFactoryProviderForO就是目前获取的工厂类.

    第二步:通过WebViewFactoryProvider的工厂类(现在已经知道了是WebViewChromiumFactoryProviderForO)创建WebViewFactoryProvider的实现类

    再回到getProvider中看看下面这些代码:

                    Method staticFactory = null;
                    try {
                        staticFactory = providerClass.getMethod(
                            CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
                    } catch (Exception e) {
                        if (DEBUG) {
                            Log.w(LOGTAG, "error instantiating provider with static factory method", e);
                        }
                    }
    
                    Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
                    try {
                        sProviderInstance = (WebViewFactoryProvider)
                                staticFactory.invoke(null, new WebViewDelegate());
    

    可以看出来它是通过反射WebViewChromiumFactoryProviderForO的CHROMIUM_WEBVIEW_FACTORY_METHOD方法获取WebViewFactoryProvider的实现类.

    private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
    

    源码中的CHROMIUM_WEBVIEW_FACTORY_METHOD是一个create函数.

    Ok,我们现在找到了WebViewFactoryProvider的实现类是通过高反射WebViewChromiumFactoryProviderForO调用create创建的,那下面来看看WebViewChromiumFactoryProviderForO的源码怎么实现create.
    class WebViewChromiumFactoryProviderForO extends WebViewChromiumFactoryProvider {
        public static WebViewChromiumFactoryProvider create(android.webkit.WebViewDelegate delegate) {
            return new WebViewChromiumFactoryProviderForO(delegate);
        }
    
        protected WebViewChromiumFactoryProviderForO(android.webkit.WebViewDelegate delegate) {
            super(delegate);
        }
    }
    

    可以看出来WebViewChromiumFactoryProviderForO的create方法创建的其实就是它自己,但是它的很多方法都是在父类里面的,我们需要跟进父类去看看getCookieManager到底返回的是什么,才能知道CookieManager的实现类是什么.

    public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
    @Override
        public CookieManager getCookieManager() {
            synchronized (mLock) {
                if (mCookieManager == null) {
                    if (!mStarted) {
                        // We can use CookieManager without starting Chromium; the native code
                        // will bring up just the parts it needs to make this work on a temporary
                        // basis until Chromium is started for real. The temporary cookie manager
                        // needs the application context to have been set.
                        ContentMain.initApplicationContext(mWebViewDelegate.getApplication());
                    }
                    mCookieManager = new CookieManagerAdapter(new AwCookieManager());
                }
            }
            return mCookieManager;
        }
    }
    

    WebViewChromiumFactoryProvider中的getCookieManager返回的是一个CookieManagerAdapter,这里终于找到了CookieManager的实现类是CookieManagerAdapter
    继续看看CookieManagerAdapter的setCookie函数怎么实现的

    public class CookieManagerAdapter extends CookieManager {
    
    AwCookieManager mChromeCookieManager;
    
    @Override
        public void setCookie(String url, String value) {
            try {
                mChromeCookieManager.setCookie(fixupUrl(url), value);
            } catch (ParseException e) {
                Log.e(LOGTAG, "Not setting cookie due to error parsing URL: " + url, e);
            }
        }
    }
    
    @JNINamespace("android_webview")
    public final class AwCookieManager {
        /**
         * Set cookie for a given url. The old cookie with same host/path/name will
         * be removed. The new cookie will be added if it is not expired or it does
         * not have expiration which implies it is session cookie.
         * @param url The url which cookie is set for
         * @param value The value for set-cookie: in http response header
         */
       public void setCookie(final String url, final String value) {
            nativeSetCookie(url, value);
        }
       private native void nativeSetCookie(String url, String value);
    }
    

    最终走到了一个nativeSetCookie方法,它是native方法. so,目前暂时也没有办法跟进了,目前只能从注释推断这其实中文情况下面expiration是无效的.
    【附录】

    资料图

    需要资料的朋友可以加入Android架构交流QQ群聊:513088520

    点击链接加入群聊【Android移动架构总群】:加入群聊

    获取免费学习视频,学习大纲另外还有像高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等Android高阶开发资料免费分享。

    相关文章

      网友评论

        本文标题:CookieManager setCookie Expires字

        本文链接:https://www.haomeiwen.com/subject/mkqluqtx.html