教你认清HttpURLConnection里面的CookieManager,CookieStore,CookieHandler,HttpCookie,CookiePolicy
2016-09-24 14:24
387 查看
一.结构大纲。
关于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()); //获取CookieStore CookieStore 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.com 09-24 15:14:06.573 /com.rdc.zzh.networkutil E/NetworkUtil: httpCookie: value 172DB173E657AEA43F99877C03311B50:FG=1 09-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 BAIDUID 09-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
相关文章推荐
- HttpURLConnection与 CookieManager 实现Post提交请求和Cookie管理
- java httpurlconnection 发送cookie时,cookie要在Post前发送
- HttpURLConnection重定向,获取及设置cookie
- Android-HttpURLConnection自动管理cookie
- handler、HttpURLConnection、网络数据下载综合使用。
- android网络编程--HttpURLConnection(结合Handler和子线程)
- HttpURLConnection模拟登录后添加cookie读取网页
- HttpURLConnection使用cookie
- HttpUrlConnection下载cookie与访问时需要附带cookie的预留
- Android代码(Handler的运用),HttpURLConnection的应用,将url图片地址转换成图片。
- HttpURLConnection、Handler、新闻客户端案例、get和post提交数据
- httpURLConnection.getHeaderField("Set-Cookie")没有/取不到值解决方法
- Android 网络编程之---HttpClient 与 HttpURLConnection 共用cookie
- HttpURLConnection模拟登录后添加cookie读取网页
- 安卓访问网络常用的3种方式(httpClient, httpUrlConnection,android-query ajax)及cookie处理
- java httpurlconnection 发送cookie时,cookie要在Post前发送
- HttpURLConnection重定向,获取及设置cookie
- Android 网络编程之---HttpClient 与 HttpURLConnection 共用cookie
- 多线程断点下载-handler-thread-HttpURLConnection
- Android 网络编程之---HttpClient 与 HttpURLConnection 共用cookie