参考:
Android端实现Cookie机制
android-CookieHandler、CookieManager
Java SE 提供了一个主要的类来实现这一功能,java.NET.CookieHandler还有一些其他的辅助类和接口:
java.net.CookieManager,
java.Net.CookiePolicy,
java.net.CookieStore,
java.net.HttpCookie.
java.net.CookieHandler类部分源码:
//抽象类
public abstract class CookieHandler {
private static CookieHandler cookieHandler;
public synchronized static CookieHandler getDefault() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SecurityConstants.GET_COOKIEHANDLER_PERMISSION);
}
return cookieHandler;
}
public synchronized static void setDefault(CookieHandler cHandler) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SecurityConstants.SET_COOKIEHANDLER_PERMISSION);
}
cookieHandler = cHandler;
}
java.net.CookieManager类部分源码:
//继承CookieHandler
public class CookieManager extends CookieHandler
{
private CookiePolicy policyCallback;
private CookieStore cookieJar = null;
public CookieManager(CookieStore store, CookiePolicy cookiePolicy)
{
// use default cookie policy if not specify one
policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ORIGINAL_SERVER: cookiePolicy;
// if not specify CookieStore to use, use default one
if (store == null) {
cookieJar = new InMemoryCookieStore();
} else {
cookieJar = store;
}
}
}
一、开启Cookie存储
Android自身所带的HttpUrlConnection方法是默认不开启Cookie存储的。
不过我们可以用java提供的几个类来在Android中实现:可以先在所有请求之前声明:
//CookieManager构造方法:
// 第一个参数CookieStore:表示了一个用来存储cookies的地方
// null:使用默认的CookieStore:即class InMemoryCookieStore implements CookieStore,内存存储
// 第二个参数CookiePolicy:表示了一个用来接收Cookie的规则
// CookiePolicy.ACCEPT_ORIGINAL_SERVER 只接收来自对应Server的cookies.
// CookiePolicy.ACCEPT_ALL 接收所有Cookies.
// CookiePolicy.ACCEPT_NONE 不接收Cookies.
java.net.CookieHandler.setDefault(new java.net.CookieManager(null, CookiePolicy.ACCEPT_ALL));
开启此开关后,每次请求的Set-Cookie信息都会被CookieManager处理。
CookieManager又会使用第一个参数传入的CookieStore来处理Cookie的存储问题,因为此处传入了null,
系统会默认调用一个基于CookieStore实现的CookieStoreImpl类来处理Cookie的存储,这个类的只有基于内存的存储,当进程被杀死后,下次再进入应用,保存的Cookie信息就会丢失。
所以我们需要基于CookieStore这个接口实现一个具有内存和本地双存储机制的Cookie存储类。
什么是持久化的和非持久化的Cookies
我们可以将cookies分成两类:
(1) 持久化的cookies
(2) 非持久化的cookies
持久化的cookies:这可以被称为永久性的cookies,它被存储在客户端的硬盘内,直到它们失效。持久化的cookies应该被设置一个失效时间。有时,它们会一直存在直到用户删除它们。持久化的cookies通常被用来为某个系统收集一个用户的标识信息。
非持久化cookies:也可以被称之为临时性的cookies。如果没有定义失效时间,那么cookie将会被存储在浏览器的内存中。我上面展示的例子就是一个非持久的cookies。
修改一个持久化的cookies与一个非持久化的cookies并没有什么不同。它们唯一的区别是——持久化的cookies有一个失效时间的设置。
可以参考:
Fran Montiel实现的PersistentCookieStore 类:
https://gist.github.com/franmontiel/ed12a2295566b7076161
保存cookie有两种方式一种是数据库,另一种是SharedPreferences,其中http://blog.csdn.NET/junjieking/article/details/7658551 是使用数据库来保存的
二、添加cookie到请求头
当解决了Cookie的存储后,我们就需要考虑以后我们的每次请求需要在请求的消息头中加入Cookie字段。
以上用CookieStore存储下来的Cookie信息都会被保存成HttpCookie形式的信息。我们可以提取CookieStore中的信息并组合。
StringBuilder cookieBuilder = new StringBuilder();
String divider = "";
for (HttpCookie cookie : getCookies()) {
cookieBuilder.append(divider);
divider = ";";
cookieBuilder.append(cookie.getName());
cookieBuilder.append("=");
cookieBuilder.append(cookie.getValue());
}
cookieString = cookieBuilder.toString();
然后把这个cookieString在以后的请求中加入到请求头中,如果你用HttpUrlConnection,你就可以
httpUrlConnection.setRequestProperty("Cookie",cookieString);
java.net.HttpCookie 类部分源码:
package java.net;
import java.util.List;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;
import java.lang.NullPointerException; // for javadoc
import java.util.Locale;
import java.util.Objects;
// ----- BEGIN android -----
import java.util.Set;
import java.util.HashSet;
// ----- END android -----
/**
* An HttpCookie object represents an http cookie, which carries state
* information between server and user agent. Cookie is widely adopted
* to create stateful sessions.
*
* <p>There are 3 http cookie specifications:
* <blockquote>
* Netscape draft<br>
* RFC 2109 - <a href="http://www.ietf.org/rfc/rfc2109.txt">
* <i>http://www.ietf.org/rfc/rfc2109.txt</i></a><br>
* RFC 2965 - <a href="http://www.ietf.org/rfc/rfc2965.txt">
* <i>http://www.ietf.org/rfc/rfc2965.txt</i></a>
* </blockquote>
*
* <p>HttpCookie class can accept all these 3 forms of syntax.
*/
public final class HttpCookie implements Cloneable {
// ----- BEGIN android -----
private static final Set<String> RESERVED_NAMES = new HashSet<String>();
static {
RESERVED_NAMES.add("comment"); // RFC 2109 RFC 2965 RFC 6265
RESERVED_NAMES.add("commenturl"); // RFC 2965 RFC 6265
RESERVED_NAMES.add("discard"); // RFC 2965 RFC 6265
RESERVED_NAMES.add("domain"); // Netscape RFC 2109 RFC 2965 RFC 6265
RESERVED_NAMES.add("expires"); // Netscape
RESERVED_NAMES.add("httponly"); // RFC 6265
RESERVED_NAMES.add("max-age"); // RFC 2109 RFC 2965 RFC 6265
RESERVED_NAMES.add("path"); // Netscape RFC 2109 RFC 2965 RFC 6265
RESERVED_NAMES.add("port"); // RFC 2965 RFC 6265
RESERVED_NAMES.add("secure"); // Netscape RFC 2109 RFC 2965 RFC 6265
RESERVED_NAMES.add("version"); // RFC 2109 RFC 2965 RFC 6265
}
// ----- END android -----
/* ---------------- Fields -------------- */
//
// The value of the cookie itself.
//
private String name; // NAME= ... "$Name" style is reserved
private String value; // value of NAME
//
// Attributes encoded in the header's cookie fields.
//
private String comment; // Comment=VALUE ... describes cookie's use
private String commentURL; // CommentURL="http URL" ... describes cookie's use
private boolean toDiscard; // Discard ... discard cookie unconditionally
private String domain; // Domain=VALUE ... domain that sees cookie
private long maxAge = MAX_AGE_UNSPECIFIED; // Max-Age=VALUE ... cookies auto-expire
private String path; // Path=VALUE ... URLs that see the cookie
private String portlist; // Port[="portlist"] ... the port cookie may be returned to
private boolean secure; // Secure ... e.g. use SSL
private boolean httpOnly; // HttpOnly ... i.e. not accessible to scripts
private int version = 1; // Version=1 ... RFC 2965 style
}
三、webview共享cooike
如果你需要让应用中打开的WebView页面也能共享使用Cookie,则需要使用android.webkit.CookieManager类来设置,简单式例代码如下。
注意,第一个参数要使用链接的host部分。这样让web端的不同页面也可以共享这些cookie。
for (HttpCookie cookie : getCookies()) {
CookieManager.getInstance().setCookie(Uri.parse(url).getHost(), cookie.toString());
}
android.webkit.CookieManager 类源码:
package android.webkit;
import android.annotation.SystemApi;
import android.net.WebAddress;
/**
* Manages the cookies used by an application's {@link WebView} instances.
* Cookies are manipulated according to RFC2109.
*/
public abstract class CookieManager {
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("doesn't implement Cloneable");
}
/**
* Gets the singleton CookieManager instance.
*
* @return the singleton CookieManager instance
*/
public static synchronized CookieManager getInstance() {
return WebViewFactory.getProvider().getCookieManager();
}
/**
* Sets whether the application's {@link WebView} instances should send and
* accept cookies.
* By default this is set to true and the WebView accepts cookies.
* <p>
* When this is true
* {@link CookieManager#setAcceptThirdPartyCookies setAcceptThirdPartyCookies} and
* {@link CookieManager#setAcceptFileSchemeCookies setAcceptFileSchemeCookies}
* can be used to control the policy for those specific types of cookie.
*
* @param accept whether {@link WebView} instances should send and accept
* cookies
*/
public abstract void setAcceptCookie(boolean accept);
/**
* Gets whether the application's {@link WebView} instances send and accept
* cookies.
*
* @return true if {@link WebView} instances send and accept cookies
*/
public abstract boolean acceptCookie();
/**
* Sets whether the {@link WebView} should allow third party cookies to be set.
* Allowing third party cookies is a per WebView policy and can be set
* differently on different WebView instances.
* <p>
* Apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below
* default to allowing third party cookies. Apps targeting
* {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later default to disallowing
* third party cookies.
*
* @param webview the {@link WebView} instance to set the cookie policy on
* @param accept whether the {@link WebView} instance should accept
* third party cookies
*/
public abstract void setAcceptThirdPartyCookies(WebView webview, boolean accept);
/**
* Gets whether the {@link WebView} should allow third party cookies to be set.
*
* @param webview the {@link WebView} instance to get the cookie policy for
* @return true if the {@link WebView} accepts third party cookies
*/
public abstract boolean acceptThirdPartyCookies(WebView webview);
/**
* Sets a cookie for the given URL. Any existing cookie with the same host,
* path and name will be replaced with the new cookie. The cookie being set
* will be ignored if it is expired.
*
* @param url the URL for which the cookie is to be set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
*/
public abstract void setCookie(String url, String value);
/**
* Sets a cookie for the given URL. Any existing cookie with the same host,
* path and name will be replaced with the new cookie. The cookie being set
* will be ignored if it is expired.
* <p>
* This method is asynchronous.
* If a {@link ValueCallback} is provided,
* {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether the cookie was set successfully.
* You can pass {@code null} as the callback if you don't need to know when the operation
* completes or whether it succeeded, and in this case it is safe to call the method from a
* thread without a Looper.
*
* @param url the URL for which the cookie is to be set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
* @param callback a callback to be executed when the cookie has been set
*/
public abstract void setCookie(String url, String value, ValueCallback<Boolean> callback);
/**
* Gets the cookies for the given URL.
*
* @param url the URL for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
*/
public abstract String getCookie(String url);
/**
* See {@link #getCookie(String)}.
*
* @param url the URL for which the cookies are requested
* @param privateBrowsing whether to use the private browsing cookie jar
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
* @hide Used by Browser and by WebViewProvider implementations.
*/
@SystemApi
public abstract String getCookie(String url, boolean privateBrowsing);
/**
* Gets cookie(s) for a given uri so that it can be set to "cookie:" in http
* request header.
*
* @param uri the WebAddress for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
* HTTP request header
* @hide Used by RequestHandle and by WebViewProvider implementations.
*/
@SystemApi
public synchronized String getCookie(WebAddress uri) {
return getCookie(uri.toString());
}
/**
* Removes all session cookies, which are cookies without an expiration
* date.
* @deprecated use {@link #removeSessionCookies(ValueCallback)} instead.
*/
public abstract void removeSessionCookie();
/**
* Removes all session cookies, which are cookies without an expiration
* date.
* <p>
* This method is asynchronous.
* If a {@link ValueCallback} is provided,
* {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether any cookies were removed.
* You can pass {@code null} as the callback if you don't need to know when the operation
* completes or whether any cookie were removed, and in this case it is safe to call the
* method from a thread without a Looper.
* @param callback a callback which is executed when the session cookies have been removed
*/
public abstract void removeSessionCookies(ValueCallback<Boolean> callback);
/**
* Removes all cookies.
* @deprecated Use {@link #removeAllCookies(ValueCallback)} instead.
*/
@Deprecated
public abstract void removeAllCookie();
/**
* Removes all cookies.
* <p>
* This method is asynchronous.
* If a {@link ValueCallback} is provided,
* {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether any cookies were removed.
* You can pass {@code null} as the callback if you don't need to know when the operation
* completes or whether any cookies were removed, and in this case it is safe to call the
* method from a thread without a Looper.
* @param callback a callback which is executed when the cookies have been removed
*/
public abstract void removeAllCookies(ValueCallback<Boolean> callback);
public abstract boolean hasCookies();
@SystemApi
public abstract boolean hasCookies(boolean privateBrowsing);
@Deprecated
public abstract void removeExpiredCookie();
public abstract void flush();
public static boolean allowFileSchemeCookies() {
return getInstance().allowFileSchemeCookiesImpl();
}
@SystemApi
protected abstract boolean allowFileSchemeCookiesImpl();
public static void setAcceptFileSchemeCookies(boolean accept) {
getInstance().setAcceptFileSchemeCookiesImpl(accept);
}
@SystemApi
protected abstract void setAcceptFileSchemeCookiesImpl(boolean accept);
}
参考:Android 控件WebView设置Cookie
Android端实现Cookie机制
工具类:
package com.dch.dai.utils;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.CookieStore;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.dch.dai.DchApplication;
import android.webkit.CookieSyncManager;
public class CookieManagerUtil extends CookieManager
{
private android.webkit.CookieManager webkitCookieManager;
public CookieManagerUtil()
{
this(null, null);
}
public CookieManagerUtil(CookieStore store, CookiePolicy cookiePolicy)
{
super(null, cookiePolicy);//net包
this.webkitCookieManager = android.webkit.CookieManager.getInstance();//webkit包
}
@Override
public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException
{
// make sure our args are valid
if ((uri == null) || (responseHeaders == null)) return;
// save our url once
String url = uri.toString();
// go over the headers
for (String headerKey : responseHeaders.keySet())
{
// ignore headers which aren't cookie related
if ((headerKey == null) || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) continue;
// process each of the headers
for (String headerValue : responseHeaders.get(headerKey))
{
this.webkitCookieManager.setCookie(url, headerValue);
}
}
}
@Override
public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) throws IOException
{
// make sure our args are valid
if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException("Argument is null");
// save our url once
String url = uri.toString();
// prepare our response
Map<String, List<String>> res = new java.util.HashMap<String, List<String>>();
// get the cookie
String cookie = this.webkitCookieManager.getCookie(url);
// return it
if (cookie != null) res.put("Cookie", Arrays.asList(cookie));
return res;
}
@Override
public CookieStore getCookieStore()
{
// we don't want anyone to work with this cookie store directly
throw new UnsupportedOperationException();
}
public void removeCookie() {//清除cooike
try {
CookieSyncManager.createInstance(DchApplication.getContext());
if (this.webkitCookieManager == null) {
this.webkitCookieManager = android.webkit.CookieManager.getInstance();
}
this.webkitCookieManager.removeAllCookie();
CookieSyncManager.getInstance().sync();
} catch (Exception e) {
}
}
}