教你认清HttpURLConnection里面的CookieManager,CookieStore,CookieHandler,HttpCookie,CookiePolicy

来源:互联网 发布:godaddy域名被转走 编辑:程序博客网 时间:2024/06/05 23:41


一.结构大纲。


关于HttpURLConnection的一些用法,相信大家都很熟练了,如果你不想用网上一些网络框架,不凡可以自己封装下HttpURLConnection,自己封装自己用感觉还是不错的。好了回到正题,大家访问服务器的话,要想保持连接,必然要涉及到Cookie的保存,而在网络请求中,谷歌爸爸早已封装了一些类来帮助我们进行Cookie的保存。现在我们来认识一下CookieManager,CookieStore,CookieHandler,HttpCookie,CookiePolicy 他们之间的区别,先看一张Visio图,


二.结构解析。


可以看到CookieHandler是一个抽象类,里面封装了用于保存Cookie获取Cookie的一系列抽象方法;而CookieManager则是CookieHandler的实现类,继承了CookieHandler,也就是CookieManager里面实现了保存Cookie获取Cookie的一系列方法,至于怎么实现,我们等下再来看源码,继续看他们的结构。CookieManager的构造方法出现了两个类分别是CookieStore 和 CookiePolicy,那么这两个类又是什么呢?可以看到CookieStore是用来存储Cookie的。这里要注意下区别,CookieManager是实现了保存Cookie获取Cookie的方法,而CookieStore是用来存储Cookie的,实际上,CookieManager保存Cookie就是通过CookieStore来保存的,它里面有CookieStore,后面源码会介绍到。继续看,知道了CookieStore是用来存储Cookie,但是它里面怎么有个HttpCookie的类?我们可以大胆猜测这个一个封装Cookie的类,确实是这样,HttpCookie根据请求的响应头,从set-cookie请求头中取出Cookie,然后拆分出name,path,domain(域名),value,集合成一起的一个类。而至于CookiePolicy,他代表的是一种Cookie保存政策,里面提供三种字段,来确定当前Cookie是否应该接受还是拒绝。可以看到CookieManager是一个采取一定的Cookie保存政策的一个Cookie管理者(没有设置CookiePolicy的话会有默认值),内部是通过CookieStore来存储Cookie。

三.源码解析。


首先先来看CookieManager
我们来看下它的源码结构:
public class CookieManager extends CookieHandler {    private CookieStore store;    private CookiePolicy policy;    private static final String VERSION_ZERO_HEADER = "Set-cookie";    private static final String VERSION_ONE_HEADER = "Set-cookie2";     ....}
首先从成员变量上看,CookieManager内部带有两个成员变量CookieStore,CookiePolicy,而下面两个常量Set-Cookie,想必就不用说了吧,很明显是Cookie的返回请求头,Set-cookie是cookie版本version为0的请求头,而Set-cookie2是cookie版本version为1的请求头,可以自行谷歌。
我们来看他的关键代码,也就是那个put(URI uri , Map<String , List<String>> responseHeaders)
 @Override    public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException {        ...//省略无关的代码        // parse and construct cookies according to the map        List<HttpCookie> cookies = parseCookie(responseHeaders);        for (HttpCookie cookie : cookies) {            // if the cookie doesn't have a domain, set one. The policy will do validation.            if (cookie.getDomain() == null) {                cookie.setDomain(uri.getHost());            }            // if the cookie doesn't have a path, set one. If it does, validate it.            if (cookie.getPath() == null) {                cookie.setPath(pathToCookiePath(uri.getPath()));            } else if (!HttpCookie.pathMatches(cookie, uri)) {                continue;            }            // if the cookie has the placeholder port list "", set the port. Otherwise validate it.            if ("".equals(cookie.getPortlist())) {                cookie.setPortlist(Integer.toString(uri.getEffectivePort()));            } else if (cookie.getPortlist() != null && !HttpCookie.portMatches(cookie, uri)) {                continue;            }            // if the cookie conforms to the policy, add it into the store            if (policy.shouldAccept(uri, cookie)) {                store.add(uri, cookie);            }        }    }

这里调用到了parseCookie(responseHeaders)这个方法,至于Map<String ,List<String>> responseHeader , 这个参数,大家看着看着是不是感觉很熟悉,没错就是 httpUrlConnection.getHeaderFields() 的返回值,该方法是返回响应头的所有头字段和值,当然我们只要Cookie的话只需要找到Set-cookie/Set-cookie2字段,我们看下这个parseCookie方法
private static List<HttpCookie> parseCookie(Map<String, List<String>> responseHeaders) {        List<HttpCookie> cookies = new ArrayList<HttpCookie>();        for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) {            String key = entry.getKey();            // Only "Set-cookie" and "Set-cookie2" pair will be parsed            if (key != null && (key.equalsIgnoreCase(VERSION_ZERO_HEADER)                    || key.equalsIgnoreCase(VERSION_ONE_HEADER))) {                // parse list elements one by one                for (String cookieStr : entry.getValue()) {                    try {                        for (HttpCookie cookie : HttpCookie.parse(cookieStr)) {                            cookies.add(cookie);                        }                    } catch (IllegalArgumentException ignored) {                        // this string is invalid, jump to the next one.                    }                }            }        }        return cookies;    }
果然没错,是 根据VERSION_ZERO_HEADER(Set-cookie)来判断的,那么既然他是根据Set-cookie来找出Cookie的,它是怎么解析出来的呢?来,我们看下他这个
for (HttpCookie cookie : HttpCookie.parse(cookieStr)) {                            cookies.add(cookie);                        }
点进去
  public static List<HttpCookie> parse(String header) {        return new CookieParser(header).parse();    }
再点击那个parse进去看,可以看到
 public List<HttpCookie> parse() {            List<HttpCookie> cookies = new ArrayList<HttpCookie>(2);            // The RI permits input without either the "Set-Cookie:" or "Set-Cookie2" headers.            boolean pre2965 = true;            if (inputLowerCase.startsWith("set-cookie2:")) {                pos += "set-cookie2:".length();                pre2965 = false;                hasVersion = true;            } else if (inputLowerCase.startsWith("set-cookie:")) {                pos += "set-cookie:".length();            }            ...//省略无关代码                    String value = readAttributeValue(pre2965 ? ";" : ",;");                HttpCookie cookie = new HttpCookie(name, value);                cookie.version = pre2965 ? 0 : 1;                cookies.add(cookie);}
可以看到,跟我们猜想的一样,HttpCookie.parse(cookieStr)中是根据Set-cookie字段来拆分Cookie的,拆分出name,value,path,domain等然后合并成一个List<HttpCookie>集合,每一个HttpCookie就代表了一个Cookie信息。
好,回到刚刚CookieManager的put方法上,put方法里面调用了
   List<HttpCookie> cookies = parseCookie(responseHeaders);
这个方法解析出响应头字段中的cookie信息,但是怎么还没保存呢?别急,继续看下去,可以看到CookieStore终于出场了
// if the cookie conforms to the policy, add it into the store            if (policy.shouldAccept(uri, cookie)) {                store.add(uri, cookie);            }
它先根据CookiePolicy的规则判断当前cookie是否要接受保存,如果是则添加到CookieStore里。到这里相比大家都有点豁然开朗的感觉吧。没错,CookieManager内部是采用CookiePolicy来定制cookie保存规则,而真正保存是使用CookieStore来保存的,至于怎么保存,大家可以自行阅读源码啦。相信大家对CookieHandler,CookieManger,CookieStore,CookiePolicy,HttpCookie已经有了大概的了解了吧,既然知道原理,那还等什么,赶紧实践下


四.Cookie实践。


eg:
CookieManager cookieManager = new CookieManager();//这个方法就可以把Cookie保存在CookieStore里面了cookieManager.put(new URI(url) , httpURLConnection.getHeaderFields());//获取CookieStoreCookieStore cookieStore = cookieManager.getCookieStore();//再拿出里面的HttpCookie for (HttpCookie httpCookie : cookieStore.getCookies()) {                    Log.e(TAG, "httpCookie: domain  " + httpCookie.getDomain());                    Log.e(TAG, "httpCookie: value  " + httpCookie.getValue());                    Log.e(TAG, "httpCookie: path  " + httpCookie.getPath());                    Log.e(TAG, "httpCookie: name  " + httpCookie.getName());                    Log.e(TAG, "httpCookie: toString  " + httpCookie.toString());                }

我们来看下打印数据
09-24 15:14:06.573 /com.rdc.zzh.networkutil E/NetworkUtil: httpCookie: domain  .baidu.com09-24 15:14:06.573 /com.rdc.zzh.networkutil E/NetworkUtil: httpCookie: value  172DB173E657AEA43F99877C03311B50:FG=109-24 15:14:06.573 /com.rdc.zzh.networkutil E/NetworkUtil: httpCookie: path  /09-24 15:14:06.573 /com.rdc.zzh.networkutil E/NetworkUtil: httpCookie: name  BAIDUID09-24 15:14:06.573 /com.rdc.zzh.networkutil E/NetworkUtil: httpCookie: toString  BAIDUID=172DB173E657AEA43F99877C03311B50:FG=1

提醒下,这里httpCookie.toString()接口就是sessionId的值哦,也就是直接把这个值方法请求头"Cookie"中就行。
好了,我们来总结下:


五.总结。


①CookieHandler是抽象类,而CookieManager是CookieHandler的实现类,内存采用CookiePolicy来定制cookie保存规则,而真正保存是使用CookieStore来保存的
②HttpCookie是Cookie的载体,带有Cookie的name,path,value,domain值
③CookiePolicy是Cookie保存规则,来决定当前Cookie是否要保存
④CookieStore则是用来存储Cookie的,也就是存储HttpCookie。

好了,到这里大家已经知道了要如何去保存Cookie了吧,也就可以自己尝试去封装HttpUrlConnection了。

这是我自己简单封装了HttpURLConnection方法
有:

* 封装了get和post方法的请求
* 请求参数的封装PostBody
* 设置了Cookie的自动保存
* 获取过程中可以选择是否带进度监听


 NetworkUtil.getInstance().get("http://www.baidu.com", new ResultListener() {         @Override         public void onResultSuccess(String success) {            Log.e(TAG, "onResultSuccess: " + success);         }        @Override        public void onResultFail(String fail) {            Log.e(TAG, "onResultFail: " + fail);        }    });


地址: https://github.com/ZengZeHong/NetworkUtil
感兴趣的可以看下。

参考资料:
谷歌官方API:https://developer.android.com/reference/java/net/CookieManager.html




1 0
原创粉丝点击