前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 中Volley二次封装并实现网络请求缓存

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

作者头像
砸漏
发布2020-10-22 11:27:28
9390
发布2020-10-22 11:27:28
举报
文章被收录于专栏:恩蓝脚本恩蓝脚本

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

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

具体使用方法为:

代码语言:javascript
复制
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,

则继续发送网络请求。

代码语言:javascript
复制
/** 
* @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写到本地文件当中

代码语言:javascript
复制
/** 
* @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—初始化哪些方法需要做缓存处理,以及缓存的有效时间

代码语言:javascript
复制
/** 
* @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需要缓存的数据

代码语言:javascript
复制
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会去请求数据

代码语言:javascript
复制
/** 
* 网络请求的工具类 
*/ 
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

代码语言:javascript
复制
/** 
* 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值,根据此值来决定是否要缓存。

代码语言:javascript
复制
/** 
* @version V1.0 <描述当前版本功能  
* @author: mcoy 
*/ 
public interface ResponseListener<T  { 
public void onResponse(T response, boolean isCache); 
} 

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档