【问题标题】:Android set up Volley to use from cacheAndroid 设置 Volley 以从缓存中使用
【发布时间】:2015-11-01 01:07:52
【问题描述】:

我正在尝试为服务器 JSON 响应创建和使用缓存。

例如:

将 JSON 对象缓存到内存中,并在我们没有互联网连接时使用它。

在下面的示例代码中,我找不到任何关于如何使用Volley 缓存它并在再次更新的服务器标头未过期时重用它的文档。

像这样:将过期设置为标头并使用缓存并尝试在过期后再次加载。

我正在尝试为此方法设置缓存机制:

private void makeJsonArryReq() {
    JsonArrayRequest req = new JsonArrayRequest(Const.URL_JSON_ARRAY,
            new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                    msgResponse.setText(response.toString());
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
        }
    });
    AppController.getInstance().addToRequestQueue(req,tag_json_arry);
}

缓存方法:

public static Cache.Entry parseIgnoreCacheHeaders(NetworkResponse response) {
    long now = System.currentTimeMillis();

    Map<String, String> headers = response.headers;
    long serverDate = 0;
    String serverEtag = null;
    String headerValue;

    headerValue = headers.get("Date");
    if (headerValue != null) {
        serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
    }

    serverEtag = headers.get("ETag");

    final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
    final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
    final long softExpire = now + cacheHitButRefreshed;
    final long ttl = now + cacheExpired;

    Cache.Entry entry = new Cache.Entry();
    entry.data = response.data;
    entry.etag = serverEtag;
    entry.softTtl = softExpire;
    entry.ttl = ttl;
    entry.serverDate = serverDate;
    entry.responseHeaders = headers;

    return entry;
}

【问题讨论】:

    标签: android caching android-volley


    【解决方案1】:

    请注意,如果网络服务支持缓存输出,则不必使用下面的CacheRequest,因为Volley会自动缓存。


    对于您的问题,我在parseCacheHeaders 中使用了一些代码(并参考了@oleksandr_yefremov 的代码)。以下代码我已经测试过了。当然,也可以用于JsonArrayRequest。希望对您有所帮助!

        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(0, mUrl, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    mTextView.setText(response.toString(5));
                } catch (JSONException e) {
                    mTextView.setText(e.toString());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
    
            }
        }) {
            @Override
            protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
                try {                    
                    Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
                    if (cacheEntry == null) {
                        cacheEntry = new Cache.Entry();
                    }
                    final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
                    final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
                    long now = System.currentTimeMillis();
                    final long softExpire = now + cacheHitButRefreshed;
                    final long ttl = now + cacheExpired;
                    cacheEntry.data = response.data;
                    cacheEntry.softTtl = softExpire;
                    cacheEntry.ttl = ttl;
                    String headerValue;
                    headerValue = response.headers.get("Date");
                    if (headerValue != null) {
                        cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
                    }
                    headerValue = response.headers.get("Last-Modified");
                    if (headerValue != null) {
                        cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
                    }
                    cacheEntry.responseHeaders = response.headers;
                    final String jsonString = new String(response.data,
                            HttpHeaderParser.parseCharset(response.headers));
                    return Response.success(new JSONObject(jsonString), cacheEntry);
                } catch (UnsupportedEncodingException e) {
                    return Response.error(new ParseError(e));
                } catch (JSONException e) {
                    return Response.error(new ParseError(e));
                }
            }
    
            @Override
            protected void deliverResponse(JSONObject response) {
                super.deliverResponse(response);
            }
    
            @Override
            public void deliverError(VolleyError error) {
                super.deliverError(error);
            }
    
            @Override
            protected VolleyError parseNetworkError(VolleyError volleyError) {
                return super.parseNetworkError(volleyError);
            }
        };
    
        MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);
    

    更新

    如果要基类,参考以下代码:

    public class CacheRequest extends Request<NetworkResponse> {
        private final Response.Listener<NetworkResponse> mListener;
        private final Response.ErrorListener mErrorListener;
    
        public CacheRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
            super(method, url, errorListener);
            this.mListener = listener;
            this.mErrorListener = errorListener;
        }
    
    
        @Override
        protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
            Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
            if (cacheEntry == null) {
                cacheEntry = new Cache.Entry();
            }
            final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
            final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
            long now = System.currentTimeMillis();
            final long softExpire = now + cacheHitButRefreshed;
            final long ttl = now + cacheExpired;
            cacheEntry.data = response.data;
            cacheEntry.softTtl = softExpire;
            cacheEntry.ttl = ttl;
            String headerValue;
            headerValue = response.headers.get("Date");
            if (headerValue != null) {
                cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
            }
            headerValue = response.headers.get("Last-Modified");
            if (headerValue != null) {
                cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
            }
            cacheEntry.responseHeaders = response.headers;
            return Response.success(response, cacheEntry);
        }
    
        @Override
        protected void deliverResponse(NetworkResponse response) {
            mListener.onResponse(response);
        }
    
        @Override
        protected VolleyError parseNetworkError(VolleyError volleyError) {
            return super.parseNetworkError(volleyError);
        }
    
        @Override
        public void deliverError(VolleyError error) {
            mErrorListener.onErrorResponse(error);
        }
    }
    

    然后在MainActivity中,可以这样调用

    CacheRequest cacheRequest = new CacheRequest(0, mUrl, new Response.Listener<NetworkResponse>() {
            @Override
            public void onResponse(NetworkResponse response) {
                try {
                    final String jsonString = new String(response.data,
                            HttpHeaderParser.parseCharset(response.headers));
                    JSONObject jsonObject = new JSONObject(jsonString);
                    mTextView.setText(jsonObject.toString(5));
                } catch (UnsupportedEncodingException | JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mTextView.setText(error.toString());
            }
        });
    
        MySingleton.getInstance(this).addToRequestQueue(cacheRequest);
    

    使用完整源代码更新:

    MainActivity.java:

    package com.example.cachevolley;
    
    import android.content.Context;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.android.volley.Cache;
    import com.android.volley.NetworkResponse;
    import com.android.volley.Request;
    import com.android.volley.RequestQueue;
    import com.android.volley.Response;
    import com.android.volley.VolleyError;
    import com.android.volley.toolbox.HttpHeaderParser;
    import com.android.volley.toolbox.Volley;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.UnsupportedEncodingException;
    
    public class MainActivity extends AppCompatActivity {
    
        private final Context mContext = this;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            final TextView textView = (TextView) findViewById(R.id.textView);
    
            RequestQueue queue = Volley.newRequestQueue(this);
            String url = "http://192.168.0.100/apitest";
    
            CacheRequest cacheRequest = new CacheRequest(0, url, new Response.Listener<NetworkResponse>() {
                @Override
                public void onResponse(NetworkResponse response) {
                    try {
                        final String jsonString = new String(response.data,
                                HttpHeaderParser.parseCharset(response.headers));
                        JSONObject jsonObject = new JSONObject(jsonString);
                        textView.setText(jsonObject.toString(5));
                        Toast.makeText(mContext, "onResponse:\n\n" + jsonObject.toString(), Toast.LENGTH_SHORT).show();
                    } catch (UnsupportedEncodingException | JSONException e) {
                        e.printStackTrace();
                    }
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Toast.makeText(mContext, "onErrorResponse:\n\n" + error.toString(), Toast.LENGTH_SHORT).show();
                }
            });
    
            // Add the request to the RequestQueue.
            queue.add(cacheRequest);
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_main, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        private class CacheRequest extends Request<NetworkResponse> {
            private final Response.Listener<NetworkResponse> mListener;
            private final Response.ErrorListener mErrorListener;
    
            public CacheRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
                super(method, url, errorListener);
                this.mListener = listener;
                this.mErrorListener = errorListener;
            }
    
    
            @Override
            protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
                Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
                if (cacheEntry == null) {
                    cacheEntry = new Cache.Entry();
                }
                final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
                final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
                long now = System.currentTimeMillis();
                final long softExpire = now + cacheHitButRefreshed;
                final long ttl = now + cacheExpired;
                cacheEntry.data = response.data;
                cacheEntry.softTtl = softExpire;
                cacheEntry.ttl = ttl;
                String headerValue;
                headerValue = response.headers.get("Date");
                if (headerValue != null) {
                    cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
                }
                headerValue = response.headers.get("Last-Modified");
                if (headerValue != null) {
                    cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
                }
                cacheEntry.responseHeaders = response.headers;
                return Response.success(response, cacheEntry);
            }
    
            @Override
            protected void deliverResponse(NetworkResponse response) {
                mListener.onResponse(response);
            }
    
            @Override
            protected VolleyError parseNetworkError(VolleyError volleyError) {
                return super.parseNetworkError(volleyError);
            }
    
            @Override
            public void deliverError(VolleyError error) {
                mErrorListener.onErrorResponse(error);
            }
        }
    }
    

    清单文件:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.cachevolley" >
    
        <uses-permission android:name="android.permission.INTERNET" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    布局文件:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_world" />
    
    </RelativeLayout>
    

    【讨论】:

    • 谢谢先生。如何在您定义的语法中使用protected Response&lt;JSONObject&gt;,例如我将代码粘贴为Volley 单例类。我想在来自服务器和所有类的所有请求中使用这个定义缓存方法
    • 目前,我没有环境可以测试,但我认为,您可以创建一个自定义基类,扩展 Request 或 Request。在这个类中,像上面一样覆盖parseNetworkResponse。所有其他请求/类将使用/扩展基类。
    • 另一种方式参见第 2 次更新。
    • @LOG_TAG 我想只是为了让它更独特,如果你可以调试,你会发现localfilename看起来像602197949-1512839413。您是否同意它仅比602197949“更独特”? :) 。在 Javadoc 中,@return A pseudo-unique filename
    • 对于所有未定义cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue); 的人,请确保使用最新版本的 volley,检查您是否在任何地方都没有旧的 volley compile files('libs/httpmime-4.3.jar')。如果是,请将其删除并在您的 gradle 中将其替换为 dependencies { ... compile 'com.android.volley:volley:1.0.0' }
    【解决方案2】:

    如果响应是使用字符串请求从服务器获取的,那么只需替换行

    return Response.success(new JSONObject(jsonString), cacheEntry);
    

    有了这个

    return Response.success(new String(jsonString), cacheEntry);
    

    它适用于我的情况。试试你自己的代码。

    【讨论】:

      猜你喜欢
      • 2013-05-22
      • 2017-10-17
      • 2013-05-22
      • 2023-03-29
      • 2016-04-16
      • 2017-01-19
      • 1970-01-01
      • 2015-02-21
      • 1970-01-01
      相关资源
      最近更新 更多