版权所有,转载请注明出处:linzhiyong
https://blog.csdn.net/u012527802/article/details/81220301 https://www.jianshu.com/p/23b35d403148
相关文章
1、OkHttp3入门介绍:https://www.jianshu.com/p/af144d662bfd
2、OkHttp3入门介绍之Cookie持久化:https://www.jianshu.com/p/23b35d403148
前面文章介绍了OkHttp3的基本用法,GET/PST请求、上传下载文件等等,本章节主要介绍基于内存和本地缓存的Cookie管理。
官网:http://square.github.io/okhttp/
Github:https://github.com/square/okhttp
OkHttp3Demo传送门:https://github.com/linzhiyong/OkHttp3Demo
服务端Demo传送门:https://github.com/linzhiyong/SpringMVCDemo
本章主要从以下几个方面介绍:
OkHttp提供了用于管理Cookie的接口CookieJar
,看一下接口的内部结构:
public interface CookieJar {
/** 内部默认实现,不做任何操作. */
CookieJar NO_COOKIES = new CookieJar() {
@Override public void saveFromResponse(HttpUrl url, List cookies) {
}
@Override public List loadForRequest(HttpUrl url) {
return Collections.emptyList();
}
};
/** 调用网络请求,获取到cookie相关信息后,okhttp会回调该方法,此处可以缓存或者持久化cookie */
void saveFromResponse(HttpUrl url, List cookies);
/** 请求时,okhttp会通过该方法,获取对应的cookie */
List loadForRequest(HttpUrl url);
}
从上面可以看出,CookieJar
接口提供了saveFromResponse
、loadForRequest
两个方法,还有一个内部类默认实现NO_COOKIES
。
1)saveFromResponse
方法:当网络请求返回结果后,内部会解析Header并获取cookie相关信息,同时回调该方法,此处可以缓存或者持久化cookie,下面看一下调用源码:
public final class BridgeInterceptor implements Interceptor {
// 此处省略
@Override public Response intercept(Chain chain) throws IOException {
// 此处省略...
// 通过cookieJar接口的loadForRequest方法获取url对应的cookie
List cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
// 如果获取到的cookie不为空,则设置到请求头中
requestBuilder.header("Cookie", cookieHeader(cookies));
}
// 此处省略
}
}
2)loadForRequest
方法:当网络请求时,okhttp会通过该方法,获取对应cookie,下面看一下调用源码:
public final class BridgeInterceptor implements Interceptor {
// 此处省略
@Override public Response intercept(Chain chain) throws IOException {
// 此处省略...
// 此处获取请求的响应对象
Response networkResponse = chain.proceed(requestBuilder.build());
// 解析响应头里的信息
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
}
}
public final class HttpHeaders {
public static void receiveHeaders(CookieJar cookieJar, HttpUrl url, Headers headers) {
if (cookieJar == CookieJar.NO_COOKIES) return;
// 解析响应头里的信息
List cookies = Cookie.parseAll(url, headers);
if (cookies.isEmpty()) return;
// 调用cookieJar接口的saveFromResponse方法,下发cookie
cookieJar.saveFromResponse(url, cookies);
}
}
3)如果开发者在初始化OkHtpClient
时没有自定义CookieJar,默认不会进行cookie操作,看一下OkHttpClient
的构造器实现;
public static final class Builder {
CookieJar cookieJar;
public Builder() {
// 默认使用自带cookie管理器,没有做任何cookie处理
cookieJar = CookieJar.NO_COOKIES;
}
}
这里我仿照 android-async-http 的Cookie管理机制PersistentCookieStore
进行改造;
实现逻辑
1、定义用于管理Cookie的接口CookieStore
;
2、定义CookieJarImpl
类实现CookieJar
接口,然后用CookieStore
去接管saveFromResponse
、loadForRequest
这两个方法;
3、定义PersistentCookieStore
类实现CookieStore
接口,用于管理Cookie;
4、将PersistentCookieStore
对象设置到OkHttpClient
中;
具体实现
1、定义CookieStore
接口:
/**
* Cookie缓存接口
*
* @author linzhiyong
* @email [email protected]
* @blog https://blog.csdn.net/u012527802
* @desc
*/
public interface CookieStore {
/** 添加cookie */
void add(HttpUrl httpUrl, Cookie cookie);
/** 添加指定httpurl cookie集合 */
void add(HttpUrl httpUrl, List cookies);
/** 根据HttpUrl从缓存中读取cookie集合 */
List get(HttpUrl httpUrl);
/** 获取全部缓存cookie */
List getCookies();
/** 移除指定httpurl cookie集合 */
boolean remove(HttpUrl httpUrl, Cookie cookie);
/** 移除所有cookie */
boolean removeAll();
}
2、定义CookieJarImpl
类实现CookieJar
接口,然后用CookieStore
去接管saveFromResponse
、loadForRequest
这两个方法:
/**
* CookieJarImpl
*
* @author linzhiyong
* @email [email protected]
* @blog https://blog.csdn.net/u012527802
* @time 2018/7/20
* @desc
*/
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 cookies) {
this.cookieStore.add(url, cookies);
}
@Override
public synchronized List loadForRequest(HttpUrl url) {
return this.cookieStore.get(url);
}
public CookieStore getCookieStore() {
return this.cookieStore;
}
}
3、定义PersistentCookieStore
类实现CookieStore
接口,用于管理Cookie;
(注:这里仿照android-async-http库里的 PersistentCookieStore 实现)
/**
* Cookie缓存持久化实现类
*
* @author linzhiyong
* @email [email protected]
* @blog https://blog.csdn.net/u012527802
* @time 2018/7/20
* @desc
*/
public class PersistentCookieStore implements CookieStore {
private static final String LOG_TAG = "PersistentCookieStore";
private static final String COOKIE_PREFS = "CookiePrefsFile";
private static final String HOST_NAME_PREFIX = "host_";
private static final String COOKIE_NAME_PREFIX = "cookie_";
private final HashMap> cookies;
private final SharedPreferences cookiePrefs;
private boolean omitNonPersistentCookies = false;
/** Construct a persistent cookie store. */
public PersistentCookieStore(Context context) {
this.cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
this.cookies = new HashMap>();
Map tempCookieMap = new HashMap
这里面用到了SerializableCookie
,主要用于序列表cookie对象到对象流中:
/**
* 仿照android-async-http的SerializableCookie实现,用处是cookie对象与对象流的互转,保存和读取cookie
*
* @author linzhiyong
* @email [email protected]
* @blog https://blog.csdn.net/u012527802
* @time 2018/7/20
* @desc
*/
public class SerializableCookie implements Serializable {
private static final long serialVersionUID = 6374381828722046732L;
private transient final Cookie cookie;
private transient Cookie clientCookie;
public SerializableCookie(Cookie cookie) {
this.cookie = cookie;
}
public Cookie getCookie() {
Cookie bestCookie = cookie;
if (this.clientCookie != null) {
bestCookie = this.clientCookie;
}
return bestCookie;
}
/** 将cookie写到对象流中 */
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(this.cookie.name());
out.writeObject(this.cookie.value());
out.writeLong(this.cookie.expiresAt());
out.writeObject(this.cookie.domain());
out.writeObject(this.cookie.path());
out.writeBoolean(this.cookie.secure());
out.writeBoolean(this.cookie.httpOnly());
out.writeBoolean(this.cookie.hostOnly());
out.writeBoolean(this.cookie.persistent());
}
/** 从对象流中构建cookie对象 */
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
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()
.name(name)
.value(value)
.expiresAt(expiresAt)
.path(path);
builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
builder = secure ? builder.secure() : builder;
builder = httpOnly ? builder.httpOnly() : builder;
this.clientCookie = builder.build();
}
}
4、将PersistentCookieStore
对象设置到OkHttpClient
中;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cookieJar(new CookieJarImpl(new PersistentCookieStore(context)));
实现逻辑跟PersistentCookieStore
类似,只是对于Cookie的存储放在了Map中。
/**
* Cookie内存缓存实现
*
* @author linzhiyong
* @email [email protected]
* @blog https://blog.csdn.net/u012527802
* @time 2018/7/20
* @desc
*/
public class MemoryCookieStore implements CookieStore {
private static final String HOST_NAME_PREFIX = "host_";
private static final String COOKIE_NAME_PREFIX = "cookie_";
private final HashMap> cookies;
public MemoryCookieStore() {
this.cookies = new HashMap>();
}
@Override
public void add(HttpUrl httpUrl, Cookie cookie) {
if (!cookie.persistent()) {
return;
}
String name = this.cookieName(cookie);
String hostKey = this.hostName(httpUrl);
if(!this.cookies.containsKey(hostKey)) {
this.cookies.put(hostKey, new ConcurrentHashMap());
}
cookies.get(hostKey).put(name, cookie);
}
@Override
public void add(HttpUrl httpUrl, List cookies) {
for (Cookie cookie : cookies) {
if (isCookieExpired(cookie)) {
continue;
}
this.add(httpUrl, cookie);
}
}
@Override
public List get(HttpUrl httpUrl) {
return this.get(this.hostName(httpUrl));
}
@Override
public List getCookies() {
ArrayList result = new ArrayList();
for (String hostKey : this.cookies.keySet()) {
result.addAll(this.get(hostKey));
}
return result;
}
/** 获取cookie集合 */
private List get(String hostKey) {
ArrayList result = new ArrayList();
if (this.cookies.containsKey(hostKey)) {
Collection cookies = this.cookies.get(hostKey).values();
for (Cookie cookie : cookies) {
if (isCookieExpired(cookie)) {
this.remove(hostKey, cookie);
}
else {
result.add(cookie);
}
}
}
return result;
}
@Override
public boolean remove(HttpUrl httpUrl, Cookie cookie) {
return this.remove(this.hostName(httpUrl), cookie);
}
/** 从缓存中移除cookie */
private boolean remove(String hostKey, Cookie cookie) {
String name = this.cookieName(cookie);
if (this.cookies.containsKey(hostKey) && this.cookies.get(hostKey).containsKey(name)) {
// 从内存中移除httpUrl对应的cookie
this.cookies.get(hostKey).remove(name);
return true;
}
return false;
}
@Override
public boolean removeAll() {
this.cookies.clear();
return true;
}
/** 判断cookie是否失效 */
private boolean isCookieExpired(Cookie cookie) {
return cookie.expiresAt() < System.currentTimeMillis();
}
private String hostName(HttpUrl httpUrl) {
return httpUrl.host().startsWith(HOST_NAME_PREFIX) ? httpUrl.host() : HOST_NAME_PREFIX + httpUrl.host();
}
private String cookieName(Cookie cookie) {
return cookie == null ? null : cookie.name() + cookie.domain();
}
}
今天的主要就是介绍了Cookie的管理,就是从CookieJar
接口的两个方法入手,然后做了进一步的封装处理,PersistentCookieStore
和MemoryCookieStore
这两个类的逻辑实现基本一致,喜欢动手的小伙伴完全可以进一步抽象一下。