Android Volley的请求封装,实现图片内存缓存(防止OOM),数据磁盘缓存,及清除磁盘缓存

来源:互联网 发布:控制网络与现场总线 编辑:程序博客网 时间:2024/04/30 23:10

平时经常用到Volley请求网络数据,因为它确实好用,简单方便,因为项目要求也不是很苛刻,所以。。。。

呃。。。程序员总会去重构自己的代码,这不,我自己研究源码和网上的一些方法,重构了自己方便用的代码,在这顺便记录一下。

关于缓存,百度了好多,网上都是些什么研究源码的。。。呃,我想说,你们就别复制粘贴了,够多了!却很少有说这么用的。


不多废话,正题。。。

一:第一部分,实现内存缓存

1.因为图片是很占内存的,一不小心就Boom。。。Boom。。。Boom了,下面是写一个图片内存缓存的类MImgCache.java ,看代码:

import android.graphics.Bitmap;import android.util.LruCache;import com.android.volley.toolbox.ImageLoader;/** * Created by qinlang on 2015/10/21. */public class MImgCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {    private static int MAX_SIZE = 10 * 1024 * 1024;//内存缓存大小,10M    /**     * @param maxSize for caches that do not override {@link #sizeOf}, this is     *                the maximum number of entries in the cache. For all other caches,     *                this is the maximum sum of the sizes of the entries in this cache.     */    public MImgCache(int maxSize) {        super(maxSize);    }    public MImgCache() {        this(MAX_SIZE);    }    @Override    protected int sizeOf(String key, Bitmap value) {        return value.getRowBytes() * value.getHeight();    }    @Override    public Bitmap getBitmap(String url) {        return get(url);    }    @Override    public void putBitmap(String url, Bitmap bitmap) {        put(url, bitmap);    }}
代码很简单,就是使用LruCache,然后再指定内存大小什么的。

2.接着自己写了一个工厂类VolleyFactroy.java,单例模式,目的是为了一个应用程序只能有一个请求队列:

import android.content.Context;import com.android.volley.Request;import com.android.volley.RequestQueue;import com.android.volley.toolbox.Volley;/** * Created by qinlang on 2015/10/21. */public class VolleyFactroy {    private RequestQueue requestQueue;    private Context context;    private MImgCache imgCache;    private static final int MAX_DISK_CACHE = 20 * 1024 * 1024;//磁盘缓存20M    private VolleyFactroy(Context context) {        this.context = context;        requestQueue = getRequestQueue();        imgCache = new MImgCache();    }    private static VolleyFactroy instance;    /**     * 新的实例     *     * @param context     * @return     */    public static synchronized VolleyFactroy getInstance(Context context) {        if (instance == null)            instance = new VolleyFactroy(context);        return instance;    }    /**     * 获取请求队列     *     * @return     */    public RequestQueue getRequestQueue() {        if (requestQueue == null)            requestQueue = Volley.newRequestQueue(context.getApplicationContext(),                    MAX_DISK_CACHE);//实例一个请求队列,并初始化磁盘缓存大小        return requestQueue;    }    /**     * 添加下载队列     *     * @param request     * @param <T>     */    public <T> void addRequest(Request<T> request) {        getRequestQueue().add(request);    }    /**     * 取消下载队列     *     * @param tag     */    public void cancelRequest(Object tag) {        getRequestQueue().cancelAll(tag);    }    /**     * 获取内存缓存对象     *     * @return     */    public MImgCache getImgCache() {        return imgCache;    }}

呃。。。这样已经能使用内存缓存了,只要你用这个工厂类拿到的请求队列,图片加载什么的。。。还会那么容易OOM?。

其实我也不知道这样写好不好,反正写着写着就变这样了,反正还蛮好用的,我也不保证完全正确。


二:磁盘缓存部分(有三个决定因素)

当然这里不只是磁盘缓存,因为你会发现上面的代码用起来很不方便,不是吗?下面我们再对上面进一步封装,

让自己用起来so easy。。。

这次我不写工厂类了,我写一个工具类。是的,没错,你们不也最喜欢工具类了?

代码就懒得分了,全在下面,不难,该有注释的地方都有了,慢慢看。

import android.app.Application;import android.content.Context;import android.graphics.Bitmap;import android.os.Handler;import android.os.Message;import android.text.TextUtils;import android.widget.ImageView;import com.android.volley.AuthFailureError;import com.android.volley.Cache;import com.android.volley.DefaultRetryPolicy;import com.android.volley.NetworkError;import com.android.volley.NoConnectionError;import com.android.volley.ParseError;import com.android.volley.Request;import com.android.volley.RequestQueue;import com.android.volley.Response;import com.android.volley.ServerError;import com.android.volley.TimeoutError;import com.android.volley.VolleyError;import com.android.volley.toolbox.DiskBasedCache;import com.android.volley.toolbox.ImageLoader;import com.android.volley.toolbox.StringRequest;import com.zzgx.warner.utils.Lg;import java.io.File;import java.util.HashMap;import java.util.Map;/** * Volley工具类 * * @author Created by qinlang on 2016/3/3. last update at 2017.03 */public class VolleyUtils {    private static Context context;    private static RequestQueue requestQueue;    /**     * 返回数据成功     */    public static final int RESULT_SUCCESS = 200;    /**     * 返回数据失败     */    public static final int RESULT_FAIL = 201;    private static int timeOut = 10000;//超时时间    public static final String TAG = "volley_requestQueue";    private static final String DEFAULT_CACHE_DIR = "volley";//Volley默认缓存路径,不可更改    private static VolleyFactroy volleyFactroy;    private static VolleyUtils volleyUtils;    private final File cacheDir;    private static DiskBasedCache diskBasedCache;    private static HashMap tagMap;    public static VolleyUtils getInstance() {        if (null == context) {            throw new RuntimeException("Must be use init(context) in Application");        }        if (null == volleyUtils) {            volleyUtils = new VolleyUtils(context);        }        return volleyUtils;    }    public static void init(Context context) {        if (context == null) {            throw new IllegalArgumentException("context can not be null");        }        if (!(context instanceof Application)) {            throw new RuntimeException("context must be an Application Context");        }        VolleyUtils.context = context;        volleyUtils = new VolleyUtils(context);    }    private VolleyUtils(Context context) {        this.context = context;        if (null == volleyFactroy)            volleyFactroy = VolleyFactroy.getInstance(context);        if (null == requestQueue)            requestQueue = volleyFactroy.getRequestQueue();        cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);//实例化缓存路径        if (null == diskBasedCache)            diskBasedCache = new DiskBasedCache(cacheDir);//实例化磁盘缓存类,用于清除缓存用        if (null == tagMap)            tagMap = new HashMap<>();        tagMap.put(TAG, TAG);    }    /**     * 发送Get请求     *     * @param url     请求url     * @param handler 用于发回数据的handler,     *                参数:what = {成功{@link #RESULT_SUCCESS}|失败{@link #RESULT_FAIL}},agr1 = {method},obj = {result}     * @param method  用于判断哪个方法在调用     */    public void sendGetRequest(final String url, String tag, final Handler handler, final int method) {        sendGetRequest(url, tag, new OnRequestResultListener() {            @Override            public void onResponse(String response) {                String jsonString = response.toString();                Message message = handler.obtainMessage();                message.what = RESULT_SUCCESS;                message.arg1 = method;                message.obj = jsonString;                handler.sendMessage(message);            }            @Override            public void onErrorResponse(VolleyError error, String errString) {                error(error, handler, method);            }        });    }    /**     * 发送GET请求     *     * @param url      请求url     * @param listener 结果监听器     */    public void sendGetRequest(String url, String tag, final OnRequestResultListener listener) {        if (TextUtils.isEmpty(tag)) tag = TAG;        else cancleRequest(tag);        tagMap.put(tag, tag);        StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener() {            @Override            public void onResponse(String response) {                String jsonString = response.toString();                listener.onResponse(jsonString);            }        }, new Response.ErrorListener() {            @Override            public void onErrorResponse(VolleyError error) {                listener.onErrorResponse(error, error(error, null, 0));            }        });        request.setShouldCache(true);        request.setCacheEntry(new Cache.Entry());        request.setTag(tag);//设置标签        request.setRetryPolicy(new DefaultRetryPolicy(timeOut,                DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));//修改超时时间和重载次数        requestQueue.add(request);    }    /**     * 发送Post请求     *     * @param url     请求url     * @param params  请求的参数集     * @param handler 用于发回数据的handler,     *                参数:what = {成功{@link #RESULT_SUCCESS}|失败{@link #RESULT_FAIL}},agr1 = {method},obj = {result}     * @param method  用于判断哪个方法在调用     */    public void sendPostRequest(final String url, String tag, final HashMap params, final Handler handler, final int method) {        sendPostRequest(url, tag, params, new OnRequestResultListener() {            @Override            public void onResponse(String response) {                String jsonString = response.toString();                Message message = handler.obtainMessage();                message.what = RESULT_SUCCESS;                message.arg1 = method;                message.obj = jsonString;                handler.sendMessage(message);            }            @Override            public void onErrorResponse(VolleyError error, String errString) {                error(error, handler, method);            }        });    }    /**     * 发送POST请求     *     * @param url      请求url     * @param params   参数集     * @param listener 结果监听器     */    public void sendPostRequest(String url, String tag, final HashMap params, final OnRequestResultListener listener) {        if (TextUtils.isEmpty(tag)) tag = TAG;        else cancleRequest(tag);        tagMap.put(tag, tag);        StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener() {            @Override            public void onResponse(String response) {//成功的请求结果                String jsonString = response.toString();                listener.onResponse(jsonString);            }        }, new Response.ErrorListener() {//失败的请求结果            @Override            public void onErrorResponse(VolleyError error) {                listener.onErrorResponse(error, error(error, null, 0));            }        }) {            @Override            protected Map getParams() throws AuthFailureError {                return params;//参数从这里进去            }            /**             * 设置头部信息             * @return             * @throws AuthFailureError             */            @Override            public Map getHeaders() throws AuthFailureError {                Map params = new HashMap();                params.put("Content-Type", "application/x-www-form-urlencoded");//发送的参数以表单形式                return params;            }        };        request.setShouldCache(true);//是否启用缓存,默认已经启用,可不用设置        request.setCacheEntry(new Cache.Entry());//设置缓存实体对象,注意:这里是重点,如果为null则不会有缓存        request.setTag(tag);//设置标签        request.setRetryPolicy(new DefaultRetryPolicy(timeOut,                DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));//修改超时时间和重载次数        requestQueue.add(request);    }    /**     * 处理错误     *     * @param error     * @param handler     * @param method     */    private String error(VolleyError error, Handler handler, int method) {        String err = "请求失败!";        if (error instanceof NetworkError) {            err = "网络异常!";        } else if (error instanceof ServerError) {            err = "系统繁忙!";        } else if (error instanceof AuthFailureError) {            err = "请求验证失败!";        } else if (error instanceof ParseError) {            err = "请求解析错误!";        } else if (error instanceof NoConnectionError) {            err = "无法连接!";        } else if (error instanceof TimeoutError) {            err = "请求超时!";        }        if (null != handler) {            Message message = handler.obtainMessage();            message.what = RESULT_FAIL;            message.arg1 = method;            message.obj = err;            handler.sendMessage(message);        }        return err;    }    /**     * 加载图片     *     * @param url    图片url     * @param result 回调,bitmap加载失败时为null     */    public void loadImg(String url, final IBitmapResult result) {        getImageLoader().get(url, new ImageLoader.ImageListener() {            @Override            public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {                Bitmap bitmap = response.getBitmap();                result.onResult(bitmap);            }            @Override            public void onErrorResponse(VolleyError error) {                result.onResult(null);            }        });    }    /**     * 加载图片     *     * @param url               图片url     * @param imageView     * @param defaultImageResId 默认时显示的图片资源     * @param errorImageResId   加载错误时显示的图片资源     */    public void loadImg(String url, ImageView imageView, int defaultImageResId, int errorImageResId) {        getImageLoader().get(url, ImageLoader.getImageListener(imageView, defaultImageResId, errorImageResId));    }    /**     * 移除指定缓存     *     * @param method 方法名 请参阅{@link Request.Method},GET为0,POST为1;如此条数据用GET方式请求得到的,则参数为{@link Request.Method #GET}     * @param url    请求url     */    public void removeCache(Request.Method method, String url) {        diskBasedCache.remove(method + ":" + url);//参数为key,默认:请求方法名+“:”+url    }    /**     * 清除所有缓存     */    public void clearAllCache() {        diskBasedCache.clear();    }    /**     * 设置超时时间,默认10s     *     * @param timeOut     */    public void setTimeOut(int timeOut) {        this.timeOut = timeOut;    }    /**     * 取消全部请求     */    public void cancleAllRequest() {        if (null != requestQueue && null != tagMap) {            for (Map.Entry entry : tagMap.entrySet()) {                requestQueue.cancelAll(entry.getValue());            }        }    }    /**     * 取消请求     *     * @param tag tag     */    public void cancleRequest(String tag) {        if (null != tagMap) {            if (!TextUtils.isEmpty(tag) && tagMap.containsKey(tag)) {                requestQueue.cancelAll(tag);            }        }    }    /**     * 获取请求队列     *     * @return     */    public RequestQueue getRequestQueue() {        return requestQueue;    }    /**     * 获取工厂类     *     * @return     */    public VolleyFactroy getVolleyFactroy() {        return volleyFactroy;    }    /**     * 获取ImageLoader     *     * @return     */    public ImageLoader getImageLoader() {        return new ImageLoader(requestQueue, volleyFactroy.getImgCache());    }    /**     * 请求结果监听器     */    public interface OnRequestResultListener {        /**         * 成功的请求         *         * @param response 请求返回的字符串         */        void onResponse(String response);        /**         * 失败的请求         *         * @param error         * @param errString         */        void onErrorResponse(VolleyError error, String errString);    }    /**     * 加载图片的回调     */    public interface IBitmapResult {        /**         * 加载结果         *         * @param bitmap         */        void onResult(Bitmap bitmap);    }}
这下是不是好用很多了,即可以发送GET请求,有可以发送POST请求,还实现了磁盘缓存。


看代码,要实现磁盘缓存,你会发现里面有两个要点

1.一个是是否可缓存,true为可缓存,false为不可缓存,其实volley内部已经默认缓存,所以这个不用理会都没事。啪。。。那你还说

setShouldCache(true);
2.设置缓存实体,如果为null,即传null给它或者不重写那个方法,则不会有缓存功能。这个是第二限制条件
setCacheEntry(new Cache.Entry());


等等。。。不是说有三个因素吗?怎么就见两个?别急,老大才是最后的:

3.服务器端必须设置头部,告诉Volley这条数据是可以缓存的

response.setHeader("cache-control", "public, max-age=43200");

“缓存控制,公有(可以为私有),最大生命。。。”
没错,三个条件成立,volley才会自动帮您保存缓存,缓存也才会有效果。
上面代码可能有点乱啊,其实清除缓存可以单独用的:
     private static final String DEFAULT_CACHE_DIR = "volley";//Volley默认缓存路径     private DiskBasedCache diskBasedCache;     
     if (null == diskBasedCache)          diskBasedCache = new DiskBasedCache(cacheDir);//实例化磁盘缓存类,用于清除缓存用
     /**     * 移除指定缓存     *     * @param method 方法名 请参阅{@link com.android.volley.Request.Method},GET为0,POST为1,其它方法请加对应的即可     * @param url    请求url     */    public void removeCache(int method, String url) {        if (method == Request.Method.GET)            diskBasedCache.remove("0:" + url);//参数为key,默认为请求方法名+“:”+url        else if (method == Request.Method.POST) diskBasedCache.remove("1:" + url);    }    /**     * 清除所有缓存     */    public void clearAllCache() {        diskBasedCache.clear();    }

到这就基本结束了。。。。本人用着感觉还很方便的,我们用volley无非就是发送一些简单的请求,
用ImageLoader加载图片什么的(ImageLoader里面已经写好了,可直接拿去用)。
下面附加怎么用:
发送GET/POST请求
<pre name="code" class="html">HashMap<String, String> params = new HashMap<>();params.put("...", "...");
//...
<pre name="code" class="html"><pre name="code" class="html"><pre name="code" class="html">    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            int what = msg.what;            int method = msg.arg1;            String result = (String) msg.obj;            if (method == METHOD) {                if (what == VolleyUtils.RESULT_SUCCESS) {                                    }else if(what == VolleyUtils.RESULT_FAIL){}            }        }    };
VolleyUtils.getInstance(this).sendPostRequest(URL, params, handler, METHOD);
GET请求还更简单,就不贴了。
ImageLoader的使用
ImageLoader imageLoader = VolleyUtils.getInstance(this).getImageLoader();
imageLoader.get(imgUrl, ImageLoader.getImageListener(imageView, R.drawable.ic_default_avatar, R.drawable.ic_default_avatar));

注意:清除缓存,因为可以单独使用,你那条数据用的什么方法请求就用什么方法的参数,
如:用GET请求则volleyUtils.removeCache(0, url); GET的值为0。清除全部则顺便。
最后,顺便说一下,写这些遇到的坑,因为网上都是那些七七八八的什么源码分析,一看就恼火,那时候没找到,然后还是看了好多,收货好多
,还是不行。不找了,然后看了一晚上的源码,自己找。再加网上的一些解释,最后还是弄好了,当时那个服务器加头部的网上找半天也没见有说。
清除缓存也是,网上一点资料没擦边,还是看了一个多小时源码理清它的流程,找到方法的(其实也没那么难,但是网上就是没有),亲测清除成功。

0 0