Volley自动获取以及存储Cookie

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

你可能感兴趣的:(第三方框架总结)