Android 中Volley二次封装并实现网络请求缓存
发布时间 - 2026-01-11 03:08:06 点击率:次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);
}
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# Android
# Volley
# Volley网络请求及缓存
# Volley二次封装
# Android中网络框架简单封装的实例方法
# Android基于OkHttpUtils网络请求的二次封装
# Android Xutils3网络请求的封装详解及实例代码
# 初学Android之网络封装实例
# 不需要
# 写到
# 回调
# 自己的
# 都是
# 过了
# 都要
# 多个
# 如有
# 给我们
# 将其
# 希望能
# 很重要
# 自定义
# 重写
# 过得
# 谢谢大家
# 会去
# 重试
# 请稍后
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何快速启动建站代理加盟业务?
如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】
Laravel如何与Docker(Sail)协同开发?(环境搭建教程)
网站制作企业,网站的banner和导航栏是指什么?
香港服务器网站卡顿?如何解决网络延迟与负载问题?
Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】
如何自定义建站之星模板颜色并下载新样式?
Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】
制作电商网页,电商供应链怎么做?
香港服务器租用费用高吗?如何避免常见误区?
如何在 React 中条件性地遍历数组并渲染元素
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
Laravel路由怎么定义_Laravel核心路由系统完全入门指南
千问怎样用提示词获取健康建议_千问健康类提示词注意事项【指南】
java中使用zxing批量生成二维码立牌
Laravel怎么做缓存_Laravel Cache系统提升应用速度的策略与技巧
EditPlus中的正则表达式实战(5)
如何用狗爹虚拟主机快速搭建网站?
为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
Mybatis 中的insertOrUpdate操作
Python数据仓库与ETL构建实战_Airflow调度流程详解
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
php做exe能调用系统命令吗_执行cmd指令实现方式【详解】
laravel怎么配置和使用PHP-FPM来优化性能_laravel PHP-FPM配置与性能优化方法
今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】
教学论文网站制作软件有哪些,写论文用什么软件
?
网站建设要注意的标准 促进网站用户好感度!
如何在阿里云ECS服务器部署织梦CMS网站?
Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】
利用python获取某年中每个月的第一天和最后一天
悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】
Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲
Laravel如何使用Eloquent进行子查询
网易LOFTER官网链接 老福特网页版登录地址
Windows Hello人脸识别突然无法使用
Laravel如何从数据库删除数据_Laravel destroy和delete方法区别
C++时间戳转换成日期时间的步骤和示例代码
微博html5版本怎么弄发超话_超话进入入口及发帖格式要求【教程】
Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
米侠浏览器网页图片不显示怎么办 米侠图片加载修复
Android利用动画实现背景逐渐变暗
Laravel如何实现API资源集合?(Resource Collection教程)

