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

平时经常用到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
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值