Android 中Volley二次封装并实现网络请求缓存

Android 中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); }

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

时间: 2024-10-26 03:29:42

Android 中Volley二次封装并实现网络请求缓存的相关文章

android中Volley框架问题

问题描述 android中Volley框架问题 话说为啥我的问题基本就没解决过的 郁闷,闲话不多说,项目中有用到与服务器交互,之前都是自己写的httpurlconnection来进行传送数据,因为服务端要接受的是一个压缩后的Json字符串,之前使用如下方法将参数写入. public static String httpPost(String urlAddress,String params){ URL url=null; HttpURLConnection conn=null; Buffered

Android中Volley框架进行请求网络数据的使用_Android

问题的阐述:Android SDK中的HttpClient和HttpUrlConnection两种请求方式用来处理网络的复杂的操作,但当应用比较复杂的时候需要我们编写大量的代码处理很多东西:图像缓存,请求的调度等等: 解决:Volley就是为解决这些而生,它与2013年Google I/O大会上被提出:使得Android应用网络操作更方便更快捷:抽象了底层Http Client等实现的细节,让开发者更专注与产生RESTful Request.另外,Volley在不同的线程上异步执行所有请求而避免

Android中Volley框架进行请求网络数据的使用

问题的阐述:Android SDK中的HttpClient和HttpUrlConnection两种请求方式用来处理网络的复杂的操作,但当应用比较复杂的时候需要我们编写大量的代码处理很多东西:图像缓存,请求的调度等等: 解决:Volley就是为解决这些而生,它与2013年Google I/O大会上被提出:使得Android应用网络操作更方便更快捷:抽象了底层Http Client等实现的细节,让开发者更专注与产生RESTful Request.另外,Volley在不同的线程上异步执行所有请求而避免

Android中Volley框架下保持会话方法_Android

公司经理把我拉出来,死马当活马医,做一个安卓app,作为刚毕业几个月且只是培训了几个月的小白来说,这无疑是一个非常大的挑战,当然最大的挑战不是这个,最大的挑战时两个周做出来.这是最蛋疼的,说实话,对于有两三年的开发经验的人来说,两个周开发一个项目很简单,说不定还有很多时间用来干别的. 于是一上来就把自己给难住了,登陆还是很好做的,只要验证返回的信息就可以跳转,但是在接下来后面的数据接口连接的时候各种报错,整了两天,查了很多信息,还接受了公司老人的嘲讽和谩骂终于做出来了. 这个是基于session

Android中的二维码生成与扫描功能

0. 前言 今天这篇文章主要描述二维码的生成与扫描,使用目前流行的Zxing,为什么要讲二维码,因为二维码太普遍了,随便一个Android APP都会有二维码扫描.本篇旨在帮助有需求的同学快速完成二维码生成和扫描的功能. 1.    Zxing的使用 从github上下载项目后,可以看到整体代码结构如下: 我们只需将Zxing包下的所有代码copy一份到我们的项目中去,除了这些还需要zxing的jar包,最后相应的资源文件,包括values文件下的ids文件.raw文件中的资源文件(可以替换).

Android中Volley框架下保持会话方法

公司经理把我拉出来,死马当活马医,做一个安卓app,作为刚毕业几个月且只是培训了几个月的小白来说,这无疑是一个非常大的挑战,当然最大的挑战不是这个,最大的挑战时两个周做出来.这是最蛋疼的,说实话,对于有两三年的开发经验的人来说,两个周开发一个项目很简单,说不定还有很多时间用来干别的. 于是一上来就把自己给难住了,登陆还是很好做的,只要验证返回的信息就可以跳转,但是在接下来后面的数据接口连接的时候各种报错,整了两天,查了很多信息,还接受了公司老人的嘲讽和谩骂终于做出来了. 这个是基于session

Android中使用gps或WLAN或移动网络来定位手机?

问题描述 Android中使用gps或WLAN或移动网络来定位手机? 先判断GPS是否打开,若没有,将强制性打开,得到经纬度和时间和本机手机号.

Android中对RecyclerView Adapter封装解析

前言 关于adapter的封装,网上有很多开源库,开发的时候可以直接拿来用,省了很多事. 最近闲来无事,想着自己动手封装一个adapter. 问题 1.通常我们封装的时候,可以简化到这一步: BaseRecyclerViewAdapter adapter = new BaseRecyclerViewAdapter() { private static final int TYPE_FIR = 1; private static final int TYPE_SEC = 2; private st

Android中okhttp3.4.1+retrofit2.1.0实现离线缓存_Android

关于Retrofit+OkHttp的强大这里就不多说了,还没了解的同学可以自行去百度.这篇文章主要讲如何利用Retrofit+OkHttp来实现一个较为简单的缓存策略: 即有网环境下我们请求数据时,如果没有缓存或者缓存过期了,就去服务器拿数据,并且将新缓存保存下来,如果有缓存而且没有过期,则直接使用缓存.无网环境下我们请求数据时,缓存没过期则直接使用缓存,缓存过期了则无法使用,需要重新联网获取服务器数据. 缓存处理还是很有必要的,它有效的减少服务器负荷,降低延迟提升用户体验,同时也方便用户即使在