当服务器使用Retrofit返回相同的响应时,如何避免解析?

JJD*_*JJD 9 response http-caching http-headers retrofit okhttp

我用来避免解析遍地的服务器响应,如果它也不会改变通过计算响应的哈希值:

public class HttpClient {

    protected OkHttpClient mClient = new OkHttpClient();

    public String get(final URL url, final String[] responseHash)
        throws IOException {
        HttpURLConnection connection = new OkUrlFactory(mClient).open(url);
        InputStream inputStream = null;
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        assert messageDigest != null;
        try {
            // Read the response.
            inputStream = connection.getInputStream();
            byte[] response = readFully(inputStream);
            final byte[] digest = messageDigest.digest(response);
            responseHash[0] = Base64.encodeToString(digest, Base64.DEFAULT);
            return new String(response, Util.UTF_8);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    private byte[] readFully(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        for (int count; (count = in.read(buffer)) != -1; ) {
            out.write(buffer, 0, count);
        }
        return out.toByteArray();
    }

}
Run Code Online (Sandbox Code Playgroud)

这是响应头:

HTTP/1.1 200 OK
Server: Apache/2.4.10 (Linux/SUSE)
X-Powered-By: PHP/5.4.20
X-UA-Compatible: IE=edge
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Expires: Thu, 08 Oct 2015 16:15:09 +0000
X-Frame-Options: SAMEORIGIN
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 07 Oct 2015 16:15:09 GMT
X-Varnish: 505284843
Age: 0
Via: 1.1 varnish
Connection: keep-alive
Run Code Online (Sandbox Code Playgroud)

现在我切换到Retrofit,我想知道什么是避免解析相同响应的优雅方法?是拦截要走的路?我不负责服务器后端,也不能修改它.

axi*_*tjz 2

更新

\n\n

您可以使用 Expires 标头进行缓存控制,这样就可以避免不必要的下载。我认为这不是一个好的方法,但在这种情况下,由于您无法控制服务器端,所以这是我现在能想到的唯一方法。

\n\n
\n

实体的过期时间可以由源服务器使用 Expires 头来指定(参见第 14.21 节)。或者,可以在响应中使用 max-age 指令来指定它。当缓存的响应中存在 max-age\n 缓存控制指令时,如果响应的当前年龄大于新请求时给定的年龄值(以秒为单位),则该响应已过时资源。响应上的 max-age\n 指令意味着该响应是可缓存的(即\n“公共”),除非还存在其他一些更具限制性的缓存指令。

\n\n

如果响应同时包含 Expires 标头和 max-age 指令,则 max-age 指令将覆盖 Expires 标头,即使 Expires 标头限制性更强。对于给定的响应,此规则允许源服务器为 HTTP/1.1(或更高版本)缓存提供比 HTTP/1.0 缓存更长的过期时间。如果某些 HTTP/1.0 缓存无法正确计算年龄或过期时间(可能是由于时钟不同步),这可能会很有用。

\n\n

许多 HTTP/1.0 缓存实现会将小于或等于响应日期值的 Expires 值视为与 Cache-Control 响应指令“no-cache”等效。如果 HTTP/1.1 缓存收到这样的响应,并且该响应不包含 Cache-Control 标头字段,则它应该考虑该响应是不可缓存的,以便保持与 HTTP/1.0 服务器的兼容性。

\n\n

注意:源服务器可能希望在网络上使用相对较新的 HTTP 缓存控制功能,例如“private”指令,包括不理解该功能的旧缓存。源服务器需要将新功能与 Expires 字段相结合,该字段的值小于或等于 Date 值。这将防止较旧的缓存不正确地缓存响应。

\n
\n\n
\n\n

有不同的方法。我用这个:

\n\n
    \n
  • 在服务器响应中我们得到Etag标头并将其保存在 SharedPreferences 上。
  • \n
  • 每个服务器调用都带有“ If-None-Match”带有 Etag 值的
  • \n
  • 服务器比较 Etag 值并返回 304 - Not Modified,或者如果某些内容发生更改且内容需要更新,则返回请求本身的结果。
  • \n
\n\n

您可以使用RequestInterceptor来执行此操作,正如您所指出的:

\n\n
public class HeaderRequestInterceptor implements RequestInterceptor {\n\n    private final static String TAG = \n        HeaderRequestInterceptor.class.getSimpleName();\n\n    private SharedPreferences mPreferences;\n\n    public HeaderRequestInterceptor() {\n        mPreferences = PreferenceManager.getDefaultSharedPreferences(\n            DaoApplication.getAppContext());\n    }\n\n    @Override\n    public void intercept(RequestFacade request) {\n        String etagValue = mPreferences.getString(EtagConfig.MY_ETAG_VALUE, "");\n        request.addHeader("If-None-Match", etagValue);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

示例输出:

\n\n
Retrofit  D  ---> HTTP GET https://url.irontec.com/rest/schedule\n    D  If-None-Match:\n    D  Authorization: MyToken M2JiOGQwZGNjNWJiNWNiOTA1Yjc3YTA0YTAyMzEwYWY6OjIwMTUtMTAtMDhUMTM6MDc6MDMrMDA6MDA=\n    D  Connection: close\n\nRetrofit  D  <--- HTTP 200 https://url.irontec.com/rest/schedule (559ms)\n    D  : HTTP/1.1 200 OK\n    D  Access-Control-Allow-Credentials: true\n    D  Access-Control-Allow-Headers: Authorization, Origin, Content-Type, X-CSRF-Token\n    D  Access-Control-Allow-Methods: GET, PUT, POST, OPTIONS, DELETE\n    D  Access-Control-Allow-Origin: *\n    D  Connection: close\n    D  Content-Type: application/json; charset=UTF-8;\n    D  Date: Thu, 08 Oct 2015 13:07:07 GMT\n    D  Etag: a3145c3f85f2dca1c78f87107331c766\n    D  Server: Apache\n    D  Transfer-Encoding: chunked\n    D  X-Android-Received-Millis: 1444309624169\n    D  X-Android-Response-Source: NETWORK 200\n    D  X-Android-Sent-Millis: 1444309623870\n    D  X-Content-Type-Options: nosniff\n    D  X-Frame-Options: sameorigin\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在刷新内容时:

\n\n
Retrofit  D  ---> HTTP GET https://url.irontec.com/rest/schedule\n    D  If-None-Match: a3145c3f85f2dca1c78f87107331c766\n    D  Authorization: MyToken MGQ1OWM4YjViYTMxZWM3OGRmMDBlYTZjNmFjNDY3MmI6OjIwMTUtMTAtMDhUMTM6MTA6MDkrMDA6MDA=\n    D  Connection: close\n    D  ---> END HTTP (no body)\n\nRetrofit  D  <--- HTTP 304 https://url.irontec.com/rest/schedule (299ms)\n    D  : HTTP/1.1 304 Not Modified\n    D  Connection: close\n    D  Date: Thu, 08 Oct 2015 13:10:12 GMT\n    D  Server: Apache\n    D  X-Android-Received-Millis: 1444309809335\n    D  X-Android-Response-Source: NETWORK 304\n    D  X-Android-Sent-Millis: 1444309809163\n    D  <--- END HTTP (0-byte body)\n
Run Code Online (Sandbox Code Playgroud)\n