Volley是Google开发的一款android网络框架,非常适合小数据大并发的访问服务器,但Volley中没有提供Cookie方面的管理,幸好它是开源框架,下面就跟大家分享一下具体的修改流程:
提示:Http请求分为request和response,两者都有头信息,而Cookie的信息就是在头信息中进行传递,所以可以锁定,需要修改的地方在头信息相关的地方。
通过查看源码发现,com.android.volley.toolbox.HurlStack.java文件中存在方法performRequest,在方法的下半部分是获取到了响应头的相关信息,所以在遍历响应头
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
在这段代码的下面添加以下代码:
// 将Cookie全部获取
if ("Set-Cookie".equals(header.getKey())) {
StringBuilder mCookieStr = new StringBuilder();
for (int i = 0; i < header.getValue().size(); i++) {
if (i == header.getValue().size() - 1) {
mCookieStr.append(header.getValue().get(i));
} else {
mCookieStr.append(header.getValue().get(i)
+ "*end*");
}
}
Header h = new BasicHeader(header.getKey(),
mCookieStr.toString());
response.addHeader(h);
} else {
Header h = new BasicHeader(header.getKey(), header
.getValue().get(0));
response.addHeader(h);
}
说明:代码中默认只显示一条Set-Cookie响应头,显然这样不是我们想要的,因为Set-Cookie可能会存在多条数据,所以我们需要把所有的Set-Cookie拼接起来
添加了Cookie信息到响应头中,那么接着就要进行获取,然后判断后进行存储。继续查看源码,找到com.android.volley.toolbox.BasicNetwork.java文件中的performRequest方法,在
httpResponse = mHttpStack.performRequest(request, headers);
这段代码下面添加
// Save Cookies @author Hunter
try {
SaveCookies(httpResponse);
} catch (DateParseException e) {
e.printStackTrace();
}
这个SaveCookies的方法是处理Cookie并存储的,代码如下:
/**
* @author Hunter
* 用于保存Cookie的方法
* @param httpResponse
* @throws DateParseException
*/
public void SaveCookies(HttpResponse httpResponse) throws DateParseException {
Header[] headers = httpResponse.getHeaders("Set-Cookie");
Cookie cookieBean = new Cookie();
/** Cookie存储池 */
List mCookieContiner = new ArrayList();
if (headers == null || headers.length == 0)
return;
// 遍历Set-Cookie中的所有value
for (int i = 0; i < headers.length; i++) {
// 将每个value中以字符串"*end*"分割
String cookie = headers[i].getValue();
String[] cookievalues = cookie.split("\\*end\\*");
// 遍历每个分割的数据得到多条cookie
for (int j = 0; j < cookievalues.length; j++) {
// 分割每个cookie得到各个属性
String[] cookiePair = cookievalues[j].split(";");
cookieBean = new Cookie();
// 遍历每个属性 将对应的值插入
for (int k = 0; k < cookiePair.length; k++) {
String[] keyPair = cookiePair[k].split("=");
String key = keyPair[0].trim();
String value = keyPair.length > 1 ? keyPair[1].trim() : "";
if("expires".equals(key)){
Date parseDate = DateUtils.parseDate(value);
cookieBean.setExpires(parseDate);
}else if("domain".equals(key)){
cookieBean.setDomain(value);
}else if("path".equals(key)){
cookieBean.setPath(value);
}else{
cookieBean.setKey(key);
cookieBean.setValue(value);
}
}
// 存储每个cookie到cookie存储池中
mCookieContiner.add(cookieBean);
MyApplication.setCookies(mCookieContiner);
}
}
MyCookieDao cookieDao = MyCookieDao.getInstance(MyApplication.getInstance());
cookieDao.saveCookieLists(mCookieContiner);
}
上面的方法用到了第三方数据库框架GreenDao,其中Cookie实体类的代码是:
package com.example.android;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit.
/**
* Entity mapped to table COOKIE.
*/
public class Cookie {
private Long id;
/** Not-null value. */
private String key;
private String value;
private String domain;
private String path;
private java.util.Date expires;
public Cookie() {
}
public Cookie(Long id) {
this.id = id;
}
public Cookie(Long id, String key, String value, String domain,
String path, java.util.Date expires) {
this.id = id;
this.key = key;
this.value = value;
this.domain = domain;
this.path = path;
this.expires = expires;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/** Not-null value. */
public String getKey() {
return key;
}
/**
* Not-null value; ensure this value is available before it is saved to the
* database.
*/
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public java.util.Date getExpires() {
return expires;
}
public void setExpires(java.util.Date expires) {
this.expires = expires;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Cookie other = (Cookie) obj;
if (key == null || other.key == null) {
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
}
该方法是自动生成的,不会使用的朋友可以去查找GreenDao的相关资料。
完成后自定义一个MyCookieDao,用于操作Cookie的存储与删除等,代码如下:
package com.example.android.dao;
import java.util.List;
import org.apache.http.client.methods.HttpUriRequest;
import com.example.android.Cookie;
import com.example.android.CookieDao;
import com.example.android.DaoSession;
import com.example.android.Note;
import com.example.android.NoteDao;
import com.example.android.app.MyApplication;
import com.example.android.utils.CommonUtils;
import com.example.android.utils.LogUtils;
import android.content.Context;
/**
* 测试数据库操作类,用于操作sqlite
*
* @author Ht
*
*/
public class MyCookieDao {
private static MyCookieDao mInstance;
private DaoSession mDaoSession;
private CookieDao cookieDao;
private MyCookieDao() {
}
public static MyCookieDao getInstance(Context context) {
if (mInstance == null) {
mInstance = new MyCookieDao();
mInstance.mDaoSession = MyApplication.getDaoSession(context);
mInstance.cookieDao = mInstance.mDaoSession.getCookieDao();
}
return mInstance;
}
/**
* 增加或修改某条数据
*
* @param note
*/
public void saveCookie(Cookie cookie) {
cookieDao.insertOrReplace(cookie);
}
/**
* 批量添加数据(开启线程)
*
* @param list
*/
public void saveCookieLists(final List list) {
if (list == null || list.isEmpty()) {
return;
}
cookieDao.getSession().runInTx(new Runnable() {
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
Cookie cookie = list.get(i);
// 查询要插入的数据是否已存在
Cookie cacheCookie = queryCookie("where KEY=?",
cookie.getKey());
// 没有有效期的Cookie无需存储
if (cookie.getExpires() != null) {
// 已存在的Cookie直接更新
if (cacheCookie != null) {
cookie.setId(cacheCookie.getId());
}
cookieDao.insertOrReplace(cookie);
}
}
}
});
}
/**
* 删除指定id的数据
*
* @param id
*/
public void deleteCookie(long id) {
cookieDao.deleteByKey(id);
}
/**
* 删除指定的数据
*
* @param note
*/
public void deleteCookie(Cookie cookie) {
cookieDao.delete(cookie);
}
/**
* 删除全部数据
*/
public void deleteAllCookie() {
cookieDao.getSession().runInTx(new Runnable() {
@Override
public void run() {
cookieDao.deleteAll();
}
});
}
/**
* 查找指定数据
*
* @param id
* @return
*/
public Cookie loadCookie(long id) {
return cookieDao.load(id);
}
/**
* 查找全部数据
*
* @return
*/
public List loadAllCookie() {
return cookieDao.loadAll();
}
/**
* 获取当前Cookie
*
* @return
*/
public String getCookiesHeader(String url) {
List cookies = loadAllCookie();
StringBuilder cookieStr = new StringBuilder();
for (int i = 0; i < cookies.size(); i++) {
// 验证Cookie的有效性
if (CommonUtils.checkCookie(url, cookies.get(i))) {
// 验证通过,拼接Cookie
if (i == cookies.size() - 1) {
cookieStr.append(cookies.get(i).getKey() + "="
+ cookies.get(i).getValue());
} else {
cookieStr.append(cookies.get(i).getKey() + "="
+ cookies.get(i).getValue() + ";");
}
} else {
// 验证不通过,删除无效Cookie
deleteCookie(cookies.get(i));
}
}
LogUtils.e("从数据库中取出了cookie:" + cookieStr.toString());
return cookieStr.toString();
}
/**
* 根据条件查找多条数据
*
* @param where
* @param params
* @return
*/
public List queryCookies(String where, String... params) {
return cookieDao.queryRaw(where, params);
}
/**
* 根据条件查找单条数据
*
* @param where
* @param params
* @return
*/
public Cookie queryCookie(String where, String... params) {
List queryRaw = cookieDao.queryRaw(where, params);
if (queryRaw != null && queryRaw.size() > 0)
return queryRaw.get(0);
return null;
}
}
由于操作数据库所消耗的资源相对来说是比较大的,所以将Cookie数据临时存储在内存当中,获取数据时,如果内存中存在Cookie数据,那么就直接取出来,如果不存在才会对数据库进行读取获取数据,所以这里需要用到了自定义的Application
package com.example.android.app;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.methods.HttpUriRequest;
import android.app.Application;
import android.content.Context;
import com.example.android.Cookie;
import com.example.android.DaoMaster;
import com.example.android.DaoMaster.OpenHelper;
import com.example.android.DaoSession;
import com.example.android.dao.MyCookieDao;
import com.example.android.exception.CatchHandler;
import com.example.android.utils.CommonUtils;
import com.example.android.utils.LogUtils;
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.utils.StorageUtils;
/**
* 自定义应用入口
*
* @author Ht
*
*/
public class MyApplication extends Application {
private static DaoMaster daoMaster;
private static DaoSession daoSession;
private static Context mInstance;
private static List cookies = new ArrayList();
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
// initCatchHandler();
initImageLoader();
}
/**
* 初始化App异常处理器
*/
private void initCatchHandler() {
CatchHandler catchHandler = CatchHandler.getInstance();
catchHandler.init(getApplicationContext());
catchHandler.setParam(1, "Package", "Application Name", "Server Url",
"Dialog Msg", 0);
}
/**
* 初始化imageloader
*/
private void initImageLoader() {
File cacheDir = StorageUtils.getOwnCacheDirectory(
getApplicationContext(), "imageloader/Cache");
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true) // 加载图片时会在内存中加载缓存
.cacheOnDisk(true) // 加载图片时会在磁盘中加载缓存
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
this)
.memoryCacheExtraOptions(480, 800)
// default = device screen dimensions
.diskCacheExtraOptions(480, 800, null)
.threadPoolSize(3)
// default
.threadPriority(Thread.NORM_PRIORITY - 2)
// default
.tasksProcessingOrder(QueueProcessingType.FIFO)
// default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))
.memoryCacheSize(2 * 1024 * 1024).memoryCacheSizePercentage(13)
// default
.diskCache(new UnlimitedDiscCache(cacheDir))
// default
.diskCacheSize(50 * 1024 * 1024).diskCacheFileCount(100)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
.defaultDisplayImageOptions(defaultOptions) // default
.writeDebugLogs().build();
ImageLoader.getInstance().init(config);
}
/**
* 取得DaoMaster
*
* @param context
* @return
*/
public static DaoMaster getDaoMaster(Context context) {
if (daoMaster == null) {
OpenHelper helper = new DaoMaster.DevOpenHelper(context,
Constants.DB_NAME, null);
daoMaster = new DaoMaster(helper.getWritableDatabase());
}
return daoMaster;
}
/**
* 取得DaoSession
*
* @param context
* @return
*/
public static DaoSession getDaoSession(Context context) {
if (daoSession == null) {
if (daoMaster == null) {
daoMaster = getDaoMaster(context);
}
daoSession = daoMaster.newSession();
}
return daoSession;
}
/**
* 设置Cookie
*
* @param cookies
*/
public static void setCookies(List cookies) {
// 如果当前不存在cookie记录则直接创建
if (MyApplication.cookies == null || MyApplication.cookies.size() == 0) {
MyApplication.cookies.addAll(cookies);
} else {
// 如果已存在则遍历,替换已有的,添加没有的
for (Cookie cookie : cookies) {
// 修改标记,true为有修改,false为未修改
boolean flag = false;
if(MyApplication.cookies.contains(cookie)){
for (Cookie c : MyApplication.cookies) {
// 复写了equals方法,匹配Cookie中key相同的数据
if (cookie.equals(c)) {
// 将已有(key相同)数据替换成新数据
c.setValue(cookie.getValue());
c.setDomain(cookie.getDomain());
c.setExpires(cookie.getExpires());
c.setPath(cookie.getValue());
// 改变修改标记
flag = true;
}
}
}
// 未修改则新增数据
if (!flag) {
MyApplication.cookies.add(cookie);
}
}
}
}
/**
* 获取当前Cookie
*
* @return
*/
public static String getCookiesHeader(String url) {
List cookies;
MyCookieDao myCookieDao = null;
// 如果当前内存中存在cookie信息则直接取出,否则从数据库中读取
if (MyApplication.cookies != null && MyApplication.cookies.size() > 0) {
cookies = MyApplication.cookies;
// 请求路径
StringBuilder cookieStr = new StringBuilder();
for (int i = 0; i < cookies.size(); i++) {
// 验证Cookie的有效性
if (CommonUtils.checkCookie(url, cookies.get(i))) {
// 验证通过,拼接Cookie
if (i == cookies.size() - 1) {
cookieStr.append(cookies.get(i).getKey() + "="
+ cookies.get(i).getValue());
} else {
cookieStr.append(cookies.get(i).getKey() + "="
+ cookies.get(i).getValue() + ";");
}
}
}
LogUtils.e("从内存中取出了cookie:" + cookieStr.toString());
return cookieStr.toString();
}
myCookieDao = MyCookieDao.getInstance(MyApplication.getInstance());
return myCookieDao.getCookiesHeader(url);
}
public static Context getInstance() {
return mInstance;
}
}
Cookie存在有效期,有效域名等等属性,所以,我们需要定义一个工具类,用于验证Cookie的有效性。
/**
* 验证cookie的有效性
*
* @param url
* @param domain
* @return
*/
public static boolean checkCookie(String url, Cookie cookie) {
String rex = "\\*" + cookie.getDomain() + "\\*";
// 验证domain的有效性
if (!TextUtils.isEmpty(cookie.getDomain())) {
if (!url.matches(rex)) {
LogUtils.i("domain失效了");
return false;
}
}
// 验证生命周期的有效性
Date expires = cookie.getExpires();
if (expires == null) {
LogUtils.i(cookie.getKey() + "有效期是空");
return true;
}
LogUtils.i(cookie.getKey() + "有效期:" + expires.after(new Date()));
return expires.after(new Date());
}
关于Cookie的存储和修改已经完成了,那么接下来就需要在请求的时候带在请求头中发送给服务器了。修改com.android.volley.Request.java文件中的getHeaders方法
// 从Application中取出当前的cookie信息
Map<String, String> cookie = new HashMap<String, String>();
String cookiesHeader = MyApplication.getCookiesHeader(getUrl());
cookie.put("Cookie", cookiesHeader);
return cookie;
到这里就已经大功告成了,添加完成后Volley会自动存储Cookie信息,验证Cookie的有效期,当超过有效期时会自动删除过期Cookie。
您有什么建议,可以通过Email:[email protected]和作者Hunter交流 如果想要关注更多关于Android开发中的一些经验分享,请访问http://blog.csdn.net/ht_android