Volley二次封装,实现网络请求缓存

Android目前很多同学使用Volley请求网络数据,但是Volley没有对请求过得数据进行缓存,因此需要我们自己手动缓存。 一下就是我的一种思路,仅供参考

具体使用方法为:

HashMap<String,String> params = new HashMap<>();
params.put("id", "1");
params.put("user", "mcoy");

new NetWorkHelper(getActivity()).jacksonMethodRequest("method_id", params, new TypeReference<ReturnTemple<FirstCategories>>(){}, handler, msgId);


NetWorkHelper---对Volley的封装,首先调用CacheManager.get(methodName, params);方法获取缓存中的数据,如果数据为null,
则继续发送网络请求。
/**
 * @version V1.0 网络请求帮助类
 * @author: mcoy
 */
public final class NetWorkHelper {

    private NetWorkManager netWorkUtils;

    public NetWorkHelper(Context context){
        netWorkUtils = new NetWorkManager(context);
    }

    public static final String COMMON_ERROR_MSG = "连接超时,请稍后重试";
    public static final int COMMON_ERROR_CODE = 2;


    /**
     * 使用Jackson请求的方法
     * @param methodName
     * @param params
     * @param handler
     * @param msgId
     */
    public  void jacksonMethodRequest(final String methodName,final HashMap<String,String> params,TypeReference javaType, final Handler handler,final int msgId){

        ResponseListener listener =   new ResponseListener(){
            @Override
            public void onResponse(Object response, boolean isCache) {
                PrintLog.log(response.toString());
                if (isCache){
                    CacheManager.put(methodName, params, response);
                }
                if (handler != null) {
                    Message message = handler.obtainMessage();
                    message.what = msgId;
                    message.obj = response;
                    handler.sendMessage(message);
                }
            }
        };

        Object respone = CacheManager.get(methodName, params);
        if(respone != null){
            listener.onResponse(respone,false);
            return;
        }

        HashMap<String,String> allParams =  Config.setSign(true);
        allParams.putAll(params);
        Response.ErrorListener errorListener = new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
                if (handler != null) {
                    Message message = handler.obtainMessage();
                    message.what = msgId;
                    message.obj = COMMON_ERROR_MSG;
                    handler.sendMessage(message);
                }
            }
        };
        netWorkUtils.jacksonRequest(getUrl(methodName), allParams,javaType, listener, errorListener);
    }

    /**
     * Url直接请求
     * @param url
     * @param params
     * @param handler
     * @param msgId
     */
    public void urlRequest(String url,HashMap<String,String> params,JsonParser jsonParser,final Handler handler,final int msgId){

        request(url, true, params, jsonParser, handler, msgId);
    }

    /**
     * 通过方法请求
     * @param methodName 方法名
     * @param params 请求参数
     * @param jsonParser Json解析器
     * @param handler 回调通知
     * @param msgId 通知的Id
     */
    public void methodRequest(String methodName, final HashMap<String,String> params,final JsonParser jsonParser,final Handler handler,final int msgId){
        request(getUrl(methodName),true,params,jsonParser,handler,msgId);
    }

    /**
     * 通过方法请求
     * @param methodName 方法名
     * @param params 请求参数
     * @param jsonParser Json解析器
     * @param handler 回调通知
     * @param msgId 通知的Id
     */
    public void methodRequest(String methodName, boolean isLogin,final HashMap<String,String> params,final JsonParser jsonParser,final Handler handler,final int msgId){
        request(getUrl(methodName),isLogin,params,jsonParser,handler,msgId);
    }




    private void request(final String url, boolean isLogin,final HashMap<String,String> params,final JsonParser jsonParser,final Handler handler,final int msgId){
        final HashMap<String,String> allParams = Config.setSign(isLogin);
        allParams.putAll(params);
        Response.Listener listener = new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                /**
                 * 有些请求默认是没有parser传过来的,出参只求String,譬如联合登录等
                 * 所以加了一个else if
                 */
                Object result;
                PrintLog.log(response);
                if (jsonParser != null ) {
                    jsonParser.json2Obj(response);
                    jsonParser.temple.description = jsonParser.temple.getResultDescription();
                    result = jsonParser.temple;
                } else {
                    ReturnTemple temple = new ReturnTemple();
                    temple.issuccessful = false;
                    temple.description = COMMON_ERROR_MSG;
                    temple.result = -100;
                    result = temple;
                }
                if (handler != null) {
                    Message message = handler.obtainMessage();
                    message.what = msgId;
                    message.obj = result;
                    handler.sendMessage(message);
                }
            }
        };

        Response.ErrorListener errorListener = new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Object result;
                if (jsonParser != null) {
                    ReturnTemple temple = new ReturnTemple();
                    temple.issuccessful = false;
                    temple.description = COMMON_ERROR_MSG;
                    temple.result = COMMON_ERROR_CODE;
                    result = temple;
                } else {
                    result = COMMON_ERROR_MSG;
                }
                if (handler != null) {
                    Message message = handler.obtainMessage();
                    message.what = msgId;
                    message.obj = result;
                    handler.sendMessage(message);
                }
            }
        };
        netWorkUtils.request(url, allParams, listener, errorListener);
    }

    /**
     * 根据访求名拼装请求的Url
     * @param methodName
     * @return
     */
    private String getUrl(String methodName){
        String url = Config.getUrl();
        if (!StringUtil.isNullOrEmpty(methodName)) {
            url = url + "?method=" + methodName;
        }
        return url;
    }
}



CacheManager---将针对某一method所请求的数据缓存到本地文件当中,主要是将CacheRule写到本地文件当中
/**
 * @version V1.0 <缓存管理>
 * @author: mcoy
 */
public final class CacheManager {
    /**
     * 一个方法对应的多个Key,比如分类都是同一个方法,但是请求会不一样,可能都要缓存
     */
    private static HashMap<String, ArrayList<String>> methodKeys;
    private static final String keyFileName = "keys.pro";

    /**
     * 读取缓存的Key
     */
    public static void readCacheKey() {
        methodKeys = (HashMap<String, ArrayList<String>>) readObject(keyFileName);
        if (methodKeys == null) {
            methodKeys = new HashMap<String, ArrayList<String>>();
        }
    }

    /**
     * 保存缓存
     */
    public static void put(String method, HashMap<String, String> params, Object object) {
        long expireTime = CacheConfig.getExpireTime(method);
        if (expireTime <= 0 || methodKeys == null) {//有效时间小于0,则不需要缓存
            return;
        }
        String key = createKey(method, params);
        if (methodKeys.containsKey(method)) {
            ArrayList<String> keys = methodKeys.get(method);
            keys.add(key);
        } else {
            ArrayList<String> keys = new ArrayList<>();
            keys.add(key);
            methodKeys.put(method, keys);
        }
        writeObject(methodKeys, keyFileName);
        String fileName = key + ".pro";
        CacheRule cacheRule = new CacheRule(expireTime, object);
        LogModel.log(" put " + method + " " + key + "  " + cacheRule);
        writeObject(cacheRule, fileName);
    }

    public static Object get(String method, HashMap<String, String> params) {
        long expireTime = CacheConfig.getExpireTime(method);
        if (expireTime <= 0 || methodKeys == null || !methodKeys.containsKey(method)) {//有效时间小于0,则不需要缓存
            return null;
        }
        ArrayList<String> keys = methodKeys.get(method);
//        String saveKey = keys.get(method);
        String key = createKey(method, params);
        String fileName = key + ".pro";
//        LogModel.log("get"+method+" "+(saveKey.equals(key))+" path:"+fileName);

        CacheRule cacheRule = null;
        try {
            if (keys.contains(key)) {
                cacheRule = (CacheRule) readObject(fileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        LogModel.log("get :" + method + " " + key + " " + cacheRule);
        if (cacheRule != null && cacheRule.isExpire()) {
            return cacheRule.getData();
        } else {
            return null;
        }
    }


    public static void main(String[] args) {
        String method = "category.getCategory";
        HashMap<String, String> params = new HashMap<>();
        params.put("categoryId", "-3");
        System.out.println(createKey(method, params));
        System.out.println(CacheRule.getCurrentTime());
    }

    /**
     * 生成Key
     *
     * @param method 请求的方法名
     * @param params 私有参数(除公共参数以外的参数)
     * @return
     */
    private static String createKey(String method, HashMap<String, String> params) {
        try {
            MessageDigest digest = MessageDigest.getInstance("md5");
            digest.digest(method.getBytes("UTF-8"));
            StringBuilder builder = new StringBuilder(method);
            if (params != null && params.size() > 0) {
                Iterator<Map.Entry<String, String>> iterator = params.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> entry = iterator.next();
                    builder.append(entry.getKey()).append("=").append(entry.getValue());
                }
            }
            byte[] tempArray = digest.digest(builder.toString().getBytes("UTF-8"));
            StringBuffer keys = new StringBuffer();
            for (byte b : tempArray) {
                // 与运算
                int number = b & 0xff;// 加盐
                String str = Integer.toHexString(number);
                // System.out.println(str);
                if (str.length() == 1) {
                    keys.append("0");
                }
                keys.append(str);
            }
            return keys.toString().toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return method.toUpperCase();
    }

    /**
     * 将对象写到文件中
     *
     * @param object
     * @param fileName
     */
    private static void writeObject(Object object, String fileName) {
        try {
            ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File(CacheConfig.getCachePath() + fileName)));
            oo.writeObject(object);
            oo.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取对象
     *
     * @param fileName
     * @return
     */
    private static Object readObject(String fileName) {
        Object result = null;
        try {
            File file = new File(CacheConfig.getCachePath() + fileName);
            if (file.exists()) {
                ObjectInputStream oi = new ObjectInputStream(new FileInputStream(file));
                result = oi.readObject();
                oi.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

CacheConfig---初始化哪些方法需要做缓存处理,以及缓存的有效时间
/**
 * @version V1.0 <设置哪些类数据需要缓存>

 * @author: mcoy
 */
public final class CacheConfig {
    /**方法对应的缓存有效时间,时间是毫秒*/
    private static HashMap<String,Long> methodExpireTimes = new HashMap<String, Long>();
    private static String cachePath = null;

    static {
        methodExpireTimes.put(ConstMethod.GET_CATEGORY_LIST,30 * 60 * 1000L);
        methodExpireTimes.put(ConstMethod.GET_NEW_CATEGORY_LIST,30 * 60 * 1000L);
    }

    /**
     * 初始化缓存路径
     * @param context
     */
    public static void init(Context context){
        cachePath = context.getFilesDir().getPath()+ File.separator+"cache"+File.separator;
        File file = new File(cachePath);
        if(!file.exists()){
            file.mkdirs();
        }
        CacheManager.readCacheKey();
    }

    /**缓存路径*/
    public static String getCachePath() {
        return cachePath;
    }

    /**
     * 获取有方法对应的有效时间,如果方法没有添加缓存或者缓存时间小于0,则不添加缓存
     * @param method
     * @return
     */
    public static long getExpireTime(String method){
        if(methodExpireTimes.containsKey(method)){
            return methodExpireTimes.get(method);
        }else {
            return -1;
        }
    }
}


CacheRule---主要有两个参数,expireTime需要缓存的时间, data需要缓存的数据
public class CacheRule implements Serializable{
    /** 有效时间 */
    public long expireTime;
    /** 缓存时间*/
    public long cacheTime;
    /** 缓存数据 */
    private Object data;

    public CacheRule(long expireTime,Object data){
        cacheTime =  getCurrentTime();
        this.expireTime = expireTime;
        this.data = data;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    @Override
    public int hashCode() {
        return BeanTools.createHashcode(expireTime, cacheTime, data);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("expireTime:").append(expireTime).append(" cacheTime:").append(cacheTime)
                .append(" curTime:").append(getCurrentTime())
                .append(" isExpire:").append(isExpire()).append(" data:").append(data==null?"null":data.toString());
        return builder.toString();
    }

    /**
     * 数据是否有效
     * @return
     */
    public boolean isExpire(){
        long curTime = getCurrentTime();
        return  curTime>(expireTime+cacheTime)?false:true;
    }

    /**
     * 获取当前时间
     * @return
     */
    public static long getCurrentTime(){
//        if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT) {
//            return SystemClock.elapsedRealtimeNanos();
//        } else {
            return System.currentTimeMillis();
//        }
    }
}


NetWorkManager---往RequestQueue中添加JacksonRequest请求,然后Volley会去请求数据
/**
 * 网络请求的工具类
 */
public final class NetWorkManager {


    private RequestQueue requestQueue ;

    public NetWorkManager(Context context){
        requestQueue = Volley.newRequestQueue(context);
    }




    /**
     * 使用Jackson解析请求的方法
     * @param url
     * @param params
     * @param javaType 成功时返回的Java类型
     * @param listener
     * @param errorListener
     */
    public void jacksonRequest(final String url,final HashMap<String,String> params,TypeReference javaType, ResponseListener listener, Response.ErrorListener errorListener){
        JacksonRequest jacksonRequest = new JacksonRequest(url,javaType,params,listener,errorListener);
        requestQueue.add(jacksonRequest);
    }

    /**
     * 普通的网络请求,返回的Json
     * @param url
     * @param params
     * @param listener
     * @param errorListener
     */
    public void request(final String url,final HashMap<String,String> params,Response.Listener listener,Response.ErrorListener errorListener){

        StringRequest stringRequest = new StringRequest(Request.Method.POST,url,listener,errorListener){
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                if (PrintLog.DEBUG) {
                    Iterator<Map.Entry<String,String>> iterator = params.entrySet().iterator();
                    StringBuilder builder = new StringBuilder(url+"  ");
                    while (iterator.hasNext()){
                        Map.Entry<String,String> entry = iterator.next();
                        builder.append(entry.getKey()+":"+entry.getValue()).append("; ");
                    }
                    PrintLog.log(builder.toString());
                }
                return  params;
            }
        };
        requestQueue.add(stringRequest);
    }


}



JacksonRequest---继承Request,重写deliverResponse方法,并调用ResponseListener的onResponse方法,
并通过CacheManager.put(methodName, params, response);将获取的response缓存到CacheManager中。
这一步很重要,调用我们自己的listener,而不是Volley提供给我们的Response.Listener
/**
 * Created by mcoy
 */
public class JacksonRequest extends Request {
    private final HashMap<String,String> params;
    private final ResponseListener listener;
    private final ObjectMapper mapper;
    private final TypeReference javaType;

   public JacksonRequest(String url,TypeReference javaType,HashMap<String,String> params,ResponseListener listener,Response.ErrorListener errorListener){
       super(Method.POST,url,errorListener);
       mapper = new ObjectMapper();
       mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES,true);
       mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
       this.params = params;
       this.listener = listener;
       this.javaType = javaType;
   }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return params;
    }

    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        String json;
        try {
            json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            PrintLog.log("返回的json:" + json);
            return Response.success(mapper.readValue(json,javaType), HttpHeaderParser.parseCacheHeaders(response));
        }catch (UnsupportedEncodingException e){
            json = new String(response.data);
            PrintLog.log("json:"+json);
            try {
                return Response.success(mapper.readValue(json,javaType),HttpHeaderParser.parseCacheHeaders(response));
            } catch (IOException e1) {
                return  Response.error(new ParseError(e));
            }
        } catch (JsonParseException e) {
            PrintLog.log(e.toString());
            return  Response.error(new ParseError(e));
        } catch (JsonMappingException e) {PrintLog.log(e.toString());
            return  Response.error(new ParseError(e));
        } catch (IOException e) {PrintLog.log(e.toString());
            return  Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(Object response) {
        listener.onResponse(response,true);
    }

}



ResponseListener---自定义的一个listener接口, 在发送请求时,需要将其实现。其中才参数中比Volley的提供的listener过了一个isCache
的Boolean值,根据此值来决定是否要缓存。
/**
 * @version V1.0 <描述当前版本功能>
 * @author: mcoy
 */
public interface ResponseListener<T> {
    public void onResponse(T response, boolean isCache);
}


你可能感兴趣的:(android,网络,缓存,Volley)