天天看點

Volley離線緩存篇

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 {
                  //執行網絡請求
                  NetworkResponse networkResponse = mNetwork.performRequest(request);
                  request.addMarker("network-http-complete");
                  //省略
                  // 解析請求傳回的response工作在子線程
                  Response<?> response = request.parseNetworkResponse(networkResponse);
                  request.addMarker("network-parse-complete");
                   //如果緩存,就把 resonse存儲到緩存中
                  if (request.shouldCache() && response.cacheEntry != null) {                 
                      mCache.put(request.getCacheKey(), response.cacheEntry);
                      request.addMarker("network-cache-written");
                  }
      
                  //把請求成功結果deliver主線程 也就是成功回調
                  request.markDelivered();
                  mDelivery.postResponse(request, response);
              } catch (VolleyError volleyError) {
      
                  volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                  如果有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));
        }

    }
}
           

繼續閱讀