热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

okhttp3.4.1+retrofit2.1.0实现离线缓存的示例

关于Retrofit+OkHttp的强大这里就不多说了,还没了解的同学可以自行去百度。这篇文章主要讲如何利用Retrofit+OkHttp来

关于Retrofit+OkHttp的强大这里就不多说了,还没了解的同学可以自行去百度。这篇文章主要讲如何利用Retrofit+OkHttp来实现一个较为简单的缓存策略:

即有网环境下我们请求数据时,如果没有缓存或者缓存过期了,就去服务器拿数据,并且将新缓存保存下来,如果有缓存而且没有过期,则直接使用缓存。无网环境下我们请求数据时,缓存没过期则直接使用缓存,缓存过期了则无法使用,需要重新联网获取服务器数据。

缓存处理还是很有必要的,它有效的减少服务器负荷,降低延迟提升用户体验,同时也方便用户即使在没网络的情况下也能使用APP。

之前一直有一个疑惑,既然Retrofit已经是对OkHttp的一个封装了,为什么还一直说Retrofit+OkHttp要一起搭配使用,后来才知道其实OKHttp很重要的一个作用,就是对一些网络请求的配置,例如连接超时,读取超时,以及一些缓存配置等。

一、添加依赖

compile 'com.squareup.retrofit2:retrofit:2.1.0'
 compile 'com.squareup.retrofit2:converter-gson:2.1.0'
 compile 'com.squareup.okhttp3:okhttp:3.4.1'
 compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

二、配置OkHttpClient(设置缓存路径和缓存文件大小)

File httpCacheDirectory = new File(Environment.getExternalStorageDirectory(), "HttpCache");//这里为了方便直接把文件放在了SD卡根目录的HttpCache中,一般放在context.getCacheDir()中
int cacheSize = 10 * 1024 * 1024;//设置缓存文件大小为10M
Cache cache = new Cache(httpCacheDirectory, cacheSize);
httpClient = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时
    .readTimeout(10, TimeUnit.SECONDS)//读取超时
    .writeTimeout(10, TimeUnit.SECONDS)//写入超时
    .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加自定义缓存拦截器(后面讲解),注意这里需要使用.addNetworkInterceptor
    .cache(cache)//把缓存添加进来
    .build();

三、配置Retrofit

retrofit = new Retrofit.Builder()
    .baseUrl(baseUrl)
    .client(httpClient)//把OkHttpClient添加进来
    .addConverterFactory(GsonConverterFactory.create())
    .build();

四、编写拦截器

我们知道其实Retrofit+OkHttp的缓存主要通过拦截器实现,所以主要做的功夫也在拦截器里面。

 static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
  @Override
  public Response intercept(Chain chain) throws IOException {
   Request request = chain.request();
   //网上很多示例代码都对在request请求前对其进行无网的判断,其实无需判断,无网自动访问缓存
//   if(!NetworkUtil.getInstance().isConnected()){
//    request = request.newBuilder()
//      .cacheControl(CacheControl.FORCE_CACHE)//只访问缓存
//      .build();
//   }
   Response respOnse= chain.proceed(request);
   if (NetworkUtil.getInstance().isConnected()) {
    int maxAge = 60;//缓存失效时间,单位为秒
    return response.newBuilder()
      .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
      .header("Cache-Control", "public ,max-age=" + maxAge)
      .build();
   } else {
    //这段代码设置无效
//    int maxStale = 60 * 60 * 24 * 28; // 无网络时,设置超时为4周
//    return response.newBuilder()
//      .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
//      .removeHeader("Pragma")
//      .build();
   }
   return response;
  }
 };

到这里,其实已经可以实现了我们开头所说的缓存效果了。

但是,上面设置的每个接口缓存时间都一样,例如我现在想让不同接口的缓存数据失效时间都不一样,甚至有些接口不缓存数据,应该怎么做呢?其实也很简单

首先我们只需要在接口前面添加@Headers参数(max-age代表缓存时间,单位为秒,示例中表示缓存失效时间为60s,想要多少时间可以自行设置),不设置@Headers参数则不进行缓存。

@Headers("Cache-Control:public ,max-age=60")
@GET("getBusiness.action")//商店信息
Call getRestaurantInfo(@Query("userId") String userId,@Query("businessId") String businessId);

同时,我们的缓存拦截器也要做下简单的修改(去掉了之前的注释代码)

 static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
  @Override
  public Response intercept(Chain chain) throws IOException {
   Request request = chain.request();
   Response respOnse= chain.proceed(request);
   if (NetworkUtil.getInstance().isConnected()) {
    //获取头部信息
    String cacheCOntrol=request.cacheControl().toString();
    return response.newBuilder()
      .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
      .header("Cache-Control", cacheControl)
      .build();
   }
   return response;
  }
 };

*注意:

1.只能缓存Get请求的接口,不能缓存Post请求的接口

2.OkHttpClient需要用.addNetworkInterceptor添加缓存拦截器,不能使用.addInterceptor,也无需两者同时使用。

3.此方法无需服务器端任何操作,适用于服务器端没有其他缓存策略,如果服务器端有自己的缓存策略代码应该做相应的修改,以适应服务器端。

附上所有代码:

/**
 * 简单封装的Retroit初始化类
 */
public class initRetrofit {
 private static String baseUrl = "http://202.171.212.154:8080/hh/";
 private static OkHttpClient httpClient;
 private static Retrofit retrofit;
 public static Retrofit initRetrofit() {
  //缓存路径和大小
  File httpCacheDirectory = new File(Environment.getExternalStorageDirectory(), "HttpCache");
  int cacheSize = 10 * 1024 * 1024;
  Cache cache = new Cache(httpCacheDirectory, cacheSize);
  //日志拦截器
  HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
  interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
  httpClient = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时
    .readTimeout(10, TimeUnit.SECONDS)//读取超时
    .writeTimeout(10, TimeUnit.SECONDS)//写入超时
    .addInterceptor(interceptor)//添加日志拦截器
    .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加缓存拦截器
    .cache(cache)//把缓存添加进来
    .build();
  retrofit = new Retrofit.Builder()
    .baseUrl(baseUrl)
    .client(httpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build();
  return retrofit;
 }

 public static RetrofitAPI getService() {
  return initRetrofit().create(RetrofitAPI.class);
 }

// //缓存拦截器,不同接口不同缓存
// static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
//  @Override
//  public Response intercept(Chain chain) throws IOException {
//
//   Request request = chain.request();
//   Response respOnse= chain.proceed(request);
//
//   if (NetworkUtil.getInstance().isConnected()) {
//    String cacheCOntrol=request.cacheControl().toString();
//    return response.newBuilder()
//      .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
//      .header("Cache-Control", cacheControl)
//      .build();
//   }
//   return response;
//  }
// };
 //缓存拦截器,统一缓存60s
 static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
  @Override
  public Response intercept(Chain chain) throws IOException {
   Request request = chain.request();
   Response respOnse= chain.proceed(request);
   if (NetworkUtil.getInstance().isConnected()) {
    int maxAge = 60*60*24*2;//缓存失效时间,单位为秒
    return response.newBuilder()
      .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
      .header("Cache-Control", "public ,max-age=" + maxAge)
      .build();
   }
   return response;
  }
 };
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 创建第一个 MUI 移动应用项目
    本文将详细介绍如何使用 HBuilder 创建并运行一个基于 MUI 框架的移动应用项目。我们将逐步引导您完成项目的搭建、代码编写以及真机调试,帮助您快速入门移动应用开发。 ... [详细]
  • 梦幻西游挖图奇遇:70级项链意外触发晶清诀,3000W轻松到手
    在梦幻西游中,挖图是一项备受欢迎的活动,无论是小宝图还是高级藏宝图,都吸引了大量玩家参与。通常情况下,小宝图的数量保证了稳定的收益,但特技装备的出现往往能带来意想不到的惊喜。本文讲述了一位玩家通过挖图获得70级晶清项链的故事,最终实现了3000W的游戏币逆袭。 ... [详细]
  • 本文探讨了 RESTful API 和传统接口之间的关键差异,解释了为什么 RESTful API 在设计和实现上具有独特的优势。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • MQTT技术周报:硬件连接与协议解析
    本周开发笔记重点介绍了在新项目中使用MQTT协议进行硬件连接的技术细节,涵盖其特性、原理及实现步骤。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 邮件(带附件,模拟文件上传,跨服务器)发送核心代码1.测试邮件发送附件接口***测试邮件发送附件*@parammultipartFile*@return*@RequestMappi ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 本文探讨了在不使用服务器控件的情况下,如何通过多种方法获取并修改页面中的HTML元素值。除了常见的AJAX方式,还介绍了其他可行的技术方案。 ... [详细]
  • 解读MySQL查询执行计划的详细指南
    本文旨在帮助开发者和数据库管理员深入了解如何解读MySQL查询执行计划。通过详细的解析,您将掌握优化查询性能的关键技巧,了解各种访问类型和额外信息的含义。 ... [详细]
author-avatar
马子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有