一、关于cookie的用处:
当app需要保持用户登陆状态等,这个时候我们通常就要进行对cookie的管理来实现。如果你使用的是okhttp网络请求,那么直接对cookie进行持久化管理即可。
二、okhttp3如何进行cookie的持久化处理:
在okhttp3中新增了Cookiejar这个借口,我们可以直接进行cookie的持久化管理,具体看一下代码:
public class OkManager {
private OkHttpClient client;
private volatile static OkManager okManager;
private final String TAG = OkManager.class.getSimpleName();
OkHttpClient.Builder httpBuilder;
private List<Cookie> cookie = new ArrayList<>();
private static final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
private static List<Cookie> cookies;
public static void cleanCookie(){
cookieStore.clear();
}
public static String getCookie(){
for (int i = 0; i<cookies.size();i++){
return cookies.get(i).toString();
}
return null;
}
public OkManager() {
handler = new Handler(Looper.getMainLooper());
mGson = new Gson();
//不需要设置请求超时是调用
// client = new OkHttpClient();
//需要设置请求超时调用下面两行
httpBuilder=new OkHttpClient.Builder();
client=httpBuilder.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS) //设置超时
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
cookieStore.put(httpUrl.host(), list);
}
@Override
public List<Cookie> loadForRequest(HttpUrl httpUrl) {
cookies = cookieStore.get(httpUrl.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
.cookieJar(new CookieJarImpl(new SPCookieStore(CashLoanApp.getInstance())))
.build();
}
/**
* 采用单例获取对象
*
* @return
*/
public static OkManager getInstance() {
if (okManager == null) {
synchronized (OkManager.class) {
if (okManager == null) {
okManager = new OkManager();
}
}
}
return okManager;
}
这种做法可以简单做到cookie的持久化管理,但是当app杀死进程等一些情况下会造成cookie持久化的失效。
所以,我们要自己实现cookieJar进行cookie的存储,参考OKgo的做法,此次用sharedPreferences 对cookie进行管理。具体代码如下:
1、首先是cookieJar的实现类:
public class CookieJarImpl implements CookieJar {
private CookieStore cookieStore;
public CookieJarImpl(CookieStore cookieStore) {
if (cookieStore == null) {
throw new IllegalArgumentException("cookieStore can not be null!");
}
this.cookieStore = cookieStore;
}
@Override
public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.saveCookie(url, cookies);
}
@Override
public synchronized List<Cookie> loadForRequest(HttpUrl url) {
return cookieStore.loadCookie(url);
}
public CookieStore getCookieStore() {
return cookieStore;
}
}
2、我们再提供一个管理cookie的公共接口:
public interface CookieStore {
/** 保存url对应所有cookie */
void saveCookie(HttpUrl url, List<Cookie> cookie);
/** 保存url对应所有cookie */
void saveCookie(HttpUrl url, Cookie cookie);
/** 加载url所有的cookie */
List<Cookie> loadCookie(HttpUrl url);
/** 获取当前所有保存的cookie */
List<Cookie> getAllCookie();
/** 获取当前url对应的所有的cookie */
List<Cookie> getCookie(HttpUrl url);
/** 根据url和cookie移除对应的cookie */
boolean removeCookie(HttpUrl url, Cookie cookie);
/** 根据url移除所有的cookie */
boolean removeCookie(HttpUrl url);
/** 移除所有的cookie */
boolean removeAllCookie();
}
3、进行序列化的实现:
public class SerializableCookie implements Serializable {
private static final long serialVersionUID = 6374381323722046732L;
public static final String HOST = "host";
public static final String NAME = "name";
public static final String DOMAIN = "domain";
public static final String COOKIE = "cookie";
public String host;
public String name;
public String domain;
private transient Cookie cookie;
private transient Cookie clientCookie;
public SerializableCookie(String host, Cookie cookie) {
this.cookie = cookie;
this.host = host;
this.name = cookie.name();
this.domain = cookie.domain();
}
public Cookie getCookie() {
Cookie bestCookie = cookie;
if (clientCookie != null) {
bestCookie = clientCookie;
}
return bestCookie;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(cookie.name());
out.writeObject(cookie.value());
out.writeLong(cookie.expiresAt());
out.writeObject(cookie.domain());
out.writeObject(cookie.path());
out.writeBoolean(cookie.secure());
out.writeBoolean(cookie.httpOnly());
out.writeBoolean(cookie.hostOnly());
out.writeBoolean(cookie.persistent());
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
String name = (String) in.readObject();
String value = (String) in.readObject();
long expiresAt = in.readLong();
String domain = (String) in.readObject();
String path = (String) in.readObject();
boolean secure = in.readBoolean();
boolean httpOnly = in.readBoolean();
boolean hostOnly = in.readBoolean();
boolean persistent = in.readBoolean();
Cookie.Builder builder = new Cookie.Builder();
builder = builder.name(name);
builder = builder.value(value);
builder = builder.expiresAt(expiresAt);
builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
builder = builder.path(path);
builder = secure ? builder.secure() : builder;
builder = httpOnly ? builder.httpOnly() : builder;
clientCookie = builder.build();
}
public static SerializableCookie parseCursorToBean(Cursor cursor) {
String host = cursor.getString(cursor.getColumnIndex(HOST));
byte[] cookieBytes = cursor.getBlob(cursor.getColumnIndex(COOKIE));
Cookie cookie = bytesToCookie(cookieBytes);
return new SerializableCookie(host, cookie);
}
public static ContentValues getContentValues(SerializableCookie serializableCookie) {
ContentValues values = new ContentValues();
values.put(SerializableCookie.HOST, serializableCookie.host);
values.put(SerializableCookie.NAME, serializableCookie.name);
values.put(SerializableCookie.DOMAIN, serializableCookie.domain);
values.put(SerializableCookie.COOKIE, cookieToBytes(serializableCookie.host, serializableCookie.getCookie()));
return values;
}
/**
* cookies 序列化成 string
*
* @param cookie 要序列化
* @return 序列化之后的string
*/
public static String encodeCookie(String host, Cookie cookie) {
if (cookie == null) return null;
byte[] cookieBytes = cookieToBytes(host, cookie);
return byteArrayToHexString(cookieBytes);
}
public static byte[] cookieToBytes(String host, Cookie cookie) {
SerializableCookie serializableCookie = new SerializableCookie(host, cookie);
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(serializableCookie);
} catch (IOException e) {
Log.e("cookieToBytes: ",e.toString() );
return null;
}
return os.toByteArray();
}
/**
* 将字符串反序列化成cookies
*
* @param cookieString cookies string
* @return cookie object
*/
public static Cookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);
return bytesToCookie(bytes);
}
public static Cookie bytesToCookie(byte[] bytes) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Cookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
cookie = ((SerializableCookie) objectInputStream.readObject()).getCookie();
} catch (Exception e) {
Log.e("bytesToCookie: ",e.toString() );
}
return cookie;
}
/**
* 二进制数组转十六进制字符串
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
private static String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase(Locale.US);
}
/**
* 十六进制字符串转二进制数组
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
private static byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
/** host, name, domain 标识一个cookie是否唯一 */
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SerializableCookie that = (SerializableCookie) o;
if (host != null ? !host.equals(that.host) : that.host != null) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return domain != null ? domain.equals(that.domain) : that.domain == null;
}
@Override
public int hashCode() {
int result = host != null ? host.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (domain != null ? domain.hashCode() : 0);
return result;
}
}
4、使用 SharedPreferences 持久化存储 cookie:
public class SPCookieStore implements CookieStore {
private static final String COOKIE_PREFS = "sp_cookie"; //cookie使用prefs保存
private static final String COOKIE_NAME_PREFIX = "cookie_"; //cookie持久化的统一前缀
private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
private final SharedPreferences cookiePrefs;
public SPCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);
cookies = new HashMap<>();
//将持久化的cookies缓存到内存中,数据结构为 Map<Url.host, Map<CookieToken, Cookie>>
Map<String, ?> prefsMap = cookiePrefs.getAll();
for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
if ((entry.getValue()) != null && !entry.getKey().startsWith(COOKIE_NAME_PREFIX)) {
//获取url对应的所有cookie的key,用","分割
String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
for (String name : cookieNames) {
//根据对应cookie的Key,从xml中获取cookie的真实值
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if (encodedCookie != null) {
Cookie decodedCookie = SerializableCookie.decodeCookie(encodedCookie);
if (decodedCookie != null) {
if (!cookies.containsKey(entry.getKey())) {
cookies.put(entry.getKey(), new ConcurrentHashMap<String, Cookie>());
}
cookies.get(entry.getKey()).put(name, decodedCookie);
}
}
}
}
}
}
private String getCookieToken(Cookie cookie) {
return cookie.name() + "@" + cookie.domain();
}
/** 当前cookie是否过期 */
private static boolean isCookieExpired(Cookie cookie) {
return cookie.expiresAt() < System.currentTimeMillis();
}
/** 将url的所有Cookie保存在本地 */
@Override
public synchronized void saveCookie(HttpUrl url, List<Cookie> urlCookies) {
for (Cookie cookie : urlCookies) {
saveCookie(url, cookie);
}
}
@Override
public synchronized void saveCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) {
cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
}
//当前cookie是否过期
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
saveCookie(url, cookie, getCookieToken(cookie));
}
}
/** 保存cookie,并将cookies持久化到本地 */
private void saveCookie(HttpUrl url, Cookie cookie, String cookieToken) {
//内存缓存
cookies.get(url.host()).put(cookieToken, cookie);
//文件缓存
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX + cookieToken, SerializableCookie.encodeCookie(url.host(), cookie));
prefsWriter.apply();
}
/** 根据当前url获取所有需要的cookie,只返回没有过期的cookie */
@Override
public synchronized List<Cookie> loadCookie(HttpUrl url) {
List<Cookie> ret = new ArrayList<>();
if (!cookies.containsKey(url.host())) return ret;
Collection<Cookie> urlCookies = cookies.get(url.host()).values();
for (Cookie cookie : urlCookies) {
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
ret.add(cookie);
}
}
return ret;
}
/** 根据url移除当前的cookie */
@Override
public synchronized boolean removeCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) return false;
String cookieToken = getCookieToken(cookie);
if (!cookies.get(url.host()).containsKey(cookieToken)) return false;
//内存移除
cookies.get(url.host()).remove(cookieToken);
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + cookieToken);
}
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeCookie(HttpUrl url) {
if (!cookies.containsKey(url.host())) return false;
//内存移除
ConcurrentHashMap<String, Cookie> urlCookie = cookies.remove(url.host());
//文件移除
Set<String> cookieTokens = urlCookie.keySet();
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for (String cookieToken : cookieTokens) {
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + cookieToken);
}
}
prefsWriter.remove(url.host());
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeAllCookie() {
//内存移除
cookies.clear();
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.apply();
return true;
}
/** 获取所有的cookie */
@Override
public synchronized List<Cookie> getAllCookie() {
List<Cookie> ret = new ArrayList<>();
for (String key : cookies.keySet()) {
ret.addAll(cookies.get(key).values());
}
return ret;
}
@Override
public synchronized List<Cookie> getCookie(HttpUrl url) {
List<Cookie> ret = new ArrayList<>();
Map<String, Cookie> mapCookie = cookies.get(url.host());
if (mapCookie != null) ret.addAll(mapCookie.values());
return ret;
}
}
5、进行取出cookie和删除cookie等公共类的封装:
/**
* cookie管理工具类
*/
public class CookieUtil {
/**
* 获取指定URL对应的cookie
* @param baseUrl
* @param url
* @return
*/
public static List<Cookie> cookies(String baseUrl , String url){
//一般手动取出cookie的目的只是交给 webview 等等,非必要情况不要自己操作
CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
HttpUrl httpUrl = HttpUrl.parse(baseUrl + url);
List<Cookie> cookies = cookieStore.getCookie(httpUrl);
Log.e( "cookies: ",httpUrl.host() + "对应的cookie如下:" + cookies.toString() );
return cookies;
}
/**
* 获取所有的cookie
* @return
*/
public static List<Cookie> cookieList(){
CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
List<Cookie> allCookie = cookieStore.getAllCookie();
Log.e( "所有cookie如下: ",allCookie.toString() );
return allCookie;
}
public static String getCookie(){
CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
List<Cookie> allCookie = cookieStore.getAllCookie();
for (int i = 0; i<allCookie.size();i++){
return allCookie.get(i).toString();
}
return null;
}
/**
* 删除cookie(这里是全部删除,也可指定的地址删除)
*/
public static void removeCookie(){
// HttpUrl httpUrl = HttpUrl.parse(Urls.URL_METHOD);
CookieStore cookieStore = OkManager.getInstance().getCookieJar().getCookieStore();
// cookieStore.removeCookie(httpUrl);
cookieStore.removeAllCookie();
}
}
最后在用自己实现的CookieJarImpl代替cookieJar
public class OkManager {
private OkHttpClient client;
private volatile static OkManager okManager;
private final String TAG = OkManager.class.getSimpleName();
OkHttpClient.Builder httpBuilder;
public OkManager() {
handler = new Handler(Looper.getMainLooper());
mGson = new Gson();
//不需要设置请求超时是调用
//需要设置请求超时调用下面两行
httpBuilder=new OkHttpClient.Builder();
client=httpBuilder.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS) //设置超时
.cookieJar(new CookieJarImpl(new SPCookieStore(CashLoanApp.getInstance())))
.build();
}
/** 获取全局的cookie实例 */
public CookieJarImpl getCookieJar() {
return (CookieJarImpl) client.cookieJar();
}
/**
* 采用单例获取对象
*
* @return
*/
public static OkManager getInstance() {
if (okManager == null) {
synchronized (OkManager.class) {
if (okManager == null) {
okManager = new OkManager();
}
}
}
return okManager;
}
这样就完成了cookie的持久化操作。
三、如果需要把cookie交给webview可以这样操作 :
/**
* 把cookie交给webview
*
* @param context
* @param url
* @param cookies
*/
public static void synchronousWebCookies(Context context, String url, String cookies) {
if (!TextUtils.isEmpty(url))
if (!TextUtils.isEmpty(cookies)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.createInstance(context);
}
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();// 移除
cookieManager.removeAllCookie();
StringBuilder sbCookie = new StringBuilder();
sbCookie.append(cookies);
String cookieValue = sbCookie.toString();
Log.e("synchronousWebCookies: ", cookieValue);
cookieManager.setCookie(url, cookieValue);//为url设置cookie
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.getInstance().sync();
} else {
cookieManager.flush();
}
}
}
在webView.loadUrl(url);方法前进行以下操作:
String cookie = CookieUtil.getCookie();
synchronousWebCookies(this, url, cookie);
这样就可以完成webview携带cookie。