Volley离线缓存篇
序言
前几天项目有个需求,app离线状态下读取缓存数据,当然这个很简单,无论是存json还是对象,都可以自己写个文件或者数据库存取,但是既然用到volley框架,那么整个存取过程应该在volley中存取,我们知道volley本身自带缓存,但是在离线状态下volley请求走的是error,那么它也就不会从文件中读取数据,怎么做才能让volley在离线状态下读取它存的缓存呢?
原理分析
NetworkDispatcher
- /frameworks/volley/src/com/android/volley/NetworkDispatcher.java
- volley网络线程调度类,所有的网络线程调度都有它负责。
run()方法
@Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (true) { try { //1.执行网络请求 NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); //省略 //2. 解析请求返回的response工作在子线程 Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); //3.如果缓存,就把 resonse存储到缓存中 if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } //4.把请求成功结果deliver主线程 也就是成功回调 request.markDelivered(); mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); 5.如果有error异常 把错误deliver到主线程 也就是错误回调 parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); } } }
mNetwork.performRequest(request)
mNetwork 是com.android.volley.toolbox.BasicNetwork的一个对象其中performRequest方法为public NetworkResponse performRequest(Request<?> request) throws VolleyError { //抛出连接超时 if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } }
parseAndDeliverNetworkError(request, volleyError);
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) { error = request.parseNetworkError(error); mDelivery.postError(request, error); }
mDelivery.postError(request, error);
mDelivery是com.android.volley.ExecutorDelivery的一个对象public void postError(Request<?> request, VolleyError error) { request.addMarker("post-error"); Response<?> response = Response.error(error); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); }
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
执行的runable 代码如下public void run() { // Deliver a normal response or error, depending. if (mResponse.isSuccess()) { mRequest.deliverResponse(mResponse.result); } else { mRequest.deliverError(mResponse.error); } }
由此可看出实际上调用的还是request的deliveError方法
- 流程
- 成功的流程是1-2-3-4
- 连接超时失败的流程是1-5
那么我们就可以重写JsonObjectRequset中的 deliveError方法
解决方法
一开始的时候我只重写了deliveError 但效果不理想 在Url相同的时候volly默认的getCacheKey()返回的是Url,那么缓存的时候,所有Url相同的请求至缓存同一个文件,显然不符合我们的要求,修改的话重写getChacheKey() 通过传过来的的不同的JSONObject 的字符串值的hashcode挡住Key值就解决了这个问题
至于乱码问题请看http://blog.csdn.net/a15286856575/article/details/52063911?locationNum=1
package com.delta.news.request;
import java.io.UnsupportedEncodingException;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.android.volley.Cache;
import com.android.volley.NetworkResponse;
import com.android.volley.NoConnectionError;
import com.android.volley.ParseError;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonObjectRequest;
/**
* 1.解决没有网络,读取缓存文件 2.volley乱码
*
* @author V.Wenju.Tian
*
*/
public class MJsonObjectRequest extends JsonObjectRequest {
private JSONObject jsonObject;
public MJsonObjectRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, jsonRequest, listener, errorListener);
this.jsonObject = jsonRequest;
// TODO Auto-generated constructor stub
}
// volley缓存是根据URl进行文件缓存的,有时候url一样但参数不一样,所以要进行变化
@Override
public String getCacheKey() {
// TODO Auto-generated method stub
if(jsonObject!=null){
Log.e("nnnnmmmm", jsonObject.toString().hashCode()+"");
return getUrl() + jsonObject.toString().hashCode();
}else{
return getUrl();
}
}
@Override
public void deliverError(VolleyError error) {
// TODO Auto-generated method stub
if (error instanceof NoConnectionError) {
Cache.Entry entry = this.getCacheEntry();
if (entry != null) {
// 解析entry 并封装成response
Response<JSONObject> response = parseNetworkResponse(new NetworkResponse(
entry.data, entry.responseHeaders));
// 发送结果到主线程 也就是成功的回调
deliverResponse(response.result);
return;
}
}
super.deliverError(error);
}
/**
* 解决volley乱码的问题
*/
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
// TODO Auto-generated method stub
try {
String jsonString = new String(response.data, "UTF-8");
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}