Android Retrofit 给你的接口加上缓存

news2025/2/13 11:13:05

转载请注明出处:https://blog.csdn.net/kong_gu_you_lan/article/details/131200501?spm=1001.2014.3001.5501

本文出自 容华谢后的博客

往期回顾:

Android Retrofit + RxJava使用详解

Android 探讨一下Retrofit封装的最佳姿势

Android 谈谈我所理解的MVP

0.写在前面

最近要对接口做一些优化,于是就想着给一些频繁获取数据的接口加上缓存功能,网上搜上一搜,一般都只支持GET请求,但是因为服务器那边接口比较特殊,参数较多的获取数据接口都是用的POST,用原生的缓存方式还不行。

那只能自己实现一个,支持GET、POST请求方式,为了安全还要支持缓存数据加密,放到项目里试了试,还算比较稳定,于是便有了此篇文章。

1.流程

先看下整体的流程,还是通过OkHttp的拦截器实现的,拦截到客户端的请求,如果没有缓存,就去服务器请求数据,然后缓存到本地,然后加密。

如果有缓存,就判断下缓存的时间,没过期就返回给客户端缓存数据,过期了就再去服务器取一份,重复上面的步骤。

缓存流程

2.实现

实现一个简单的接口请求,访问百度页面,然后测试下缓存的效果:

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .client(getOkHttpClient())
    .build()

binding.btnRequest.setOnClickListener {
    val service = retrofit.create(RetrofitService::class.java)
    val call = service.request("https://www.baidu.com")
    call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(
            call: Call<ResponseBody>,
            response: Response<ResponseBody>
        ) {
            val result = response.body()?.string() ?: ""
            binding.tvResult.text = result
            Log.i("http返回:", result)
        }

        override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
        }
    })
}

主要看下getOkHttpClient()方法:

/**
 * 获取OkHttpClient
 *
 * @return OkHttpClient
 */
private fun getOkHttpClient(): OkHttpClient {
    // 定制OkHttp
    val httpClientBuilder = OkHttpClient.Builder()
    // 添加响应数据缓存拦截器
    httpClientBuilder.addInterceptor(CacheInterceptor(this, "key"))
    return httpClientBuilder.build()
}

/**
 * 缓存数据拦截器
 *
 * @param mContext Context
 * @param key      秘钥
 */
private class CacheInterceptor(
    private val mContext: Context,
    private val key: String
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
        val request = chain.request()
        val cacheKey = HttpUtils.getCacheKey(request)
        val cacheFile = File(HttpUtils.getCacheFile(mContext), cacheKey)

        // 缓存时间1小时
        val cacheTime = 3600000L
        val cacheEnable = (System.currentTimeMillis() - cacheFile.lastModified()) < cacheTime
        if (cacheEnable && cacheFile.exists() && cacheFile.length() > 0) {
            Log.i(
                "CacheInterceptor",
                "[intercept] 缓存模式 url:${HttpUtils.getRequestUrl(request)} " +
                        "过期时间:${HttpUtils.dateTimeToString(cacheFile.lastModified() + cacheTime)}"
            )
            val cache = SecurityUtils.decryptContent(cacheFile.readText(), key)
            if (cache.isNotEmpty() && cache.startsWith("{") && cache.endsWith("}")) {
                return okhttp3.Response.Builder()
                    .code(200)
                    .body(cache.toResponseBody())
                    .request(request)
                    .message("from disk cache")
                    .protocol(Protocol.HTTP_2)
                    .build()
            }
        }
        val response = chain.proceed(request)
        val responseBody = response.body ?: return response
        val data = responseBody.bytes()
        val dataString = String(data)
        // 写入缓存
        if (response.code == 200) {
            // Json数据写入缓存
            cacheFile.writeText(SecurityUtils.encryptContent(dataString, key))
        } else {
            cacheFile.writeText("")
        }
        return response.newBuilder()
            .body(data.toResponseBody(responseBody.contentType()))
            .build()
    }
}

代码不是很多,加密的逻辑放在SecurityUtils工具类中了,文章末尾下载源码就可以。

加密后的文件是这样的,文件里存储的内容是十六进制字符串:

文件目录

3.注意

这个key是秘钥,可以自己自定义,获取这个秘钥的方法,可以写在so里,也可以写成字符数组的方式,通过某种组合获取到,不要固定一个字符串就行,这样比较安全:

// 添加响应数据缓存拦截器
httpClientBuilder.addInterceptor(CacheInterceptor(this, "key"))

在写入缓存这里,判断了code是200就写入缓存,实际业务中,可能有自己的定义方式,code返回200只能证明接口通了,服务器的逻辑有自己的规则,比如在返回数据中也有一个code标记,可以在if判断里再加一个业务的判断,只有业务返回了成功,再写入缓存。

// 写入缓存
if (response.code == 200) {
    // Json数据写入缓存
    cacheFile.writeText(SecurityUtils.encryptContent(dataString, key))
} else {
    cacheFile.writeText("")
}

4.原生GET请求缓存

有的同学只想用原生的方法去缓存GET请求,在此附上代码:

override fun intercept(chain: Interceptor.Chain): Response {
    var request = chain.request()
    // GET请求
    if ("GET" == request.method) {
        return if (checkNetwork(mContext)) {
            request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build()
            val response = chain.proceed(request)
            response.newBuilder()
                .header("Cache-Control", "public, max-age=0")
                .removeHeader("Pragma")
                .build()
        } else {
            request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build()
            val response = chain.proceed(request)
            return response.newBuilder()
                .header("Cache-Control", "public, only-if-cached, max-stale=604800")
                .removeHeader("Pragma")
                .build()
        }
    }
    // POST请求
    return chain.proceed(request)
}

5.写在最后

GitHub地址:https://github.com/alidili/Demos/tree/master/RetrofitCacheDemo

到这里,Retrofit的缓存功能就介绍完了,如有问题可以给我留言评论或者在GitHub中提交Issues,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645539.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析规范总结-V2.0

结构规范及写作 报告常用结构&#xff1a; 1. 架构清晰、主次分明 数据分析报告要有一个清晰的架构&#xff0c;层次分明能降低阅读成本&#xff0c;有助于信息的传达。虽然不同类型的分析报告有其适用的呈现方式&#xff0c;但总的来说作为议论文的一种&#xff0c;大部分的分…

C语言之函数栈帧的创建与销毁讲解(2)

上一篇博客我们讲到了函数栈帧的创建与销毁&#xff08;1&#xff09;今天我们来讲解Add函数的函数栈帧相关知识 在开始本章博客之前&#xff0c;大家可以把上一篇博客的主要内容仔细复习一下 看图 第一个mov&#xff1a;把b的值放到eax里面去 第二个mov&#xff1a;把a的…

wangEditor富文本编辑器的调用开发实录(v5版本、获取HTML内容、上传图片、隐藏上传视频)

wangEditor 是一款基于原生 JavaScript 封装&#xff0c;开源免费的富文本编辑器&#xff0c;支持常规的文字排版操作、插入图片、插入视频、插入代码等功能&#xff0c;同时提供多样化的扩展功能&#xff08;如字体、颜色、表情、代码、地图等插件&#xff09;&#xff0c;支持…

SpringBoot(运维篇)

SpringBoot运维篇 SpringBoot程序的打包和运行 程序打包 SpringBoot程序是基于Maven创建的&#xff0c;在Maven中提供有打包的指令&#xff0c;叫做package。本操作可以在Idea环境下执行 mvn package打包后会产生一个与工程名类似的jar文件&#xff0c;其名称由模块名版本号…

小程序开发:如何从零开始建立你的第一个小程序

你可能有一个小程序的想法&#xff0c;但它仍然是一个想法。对于开发人员来说&#xff0c;这是一项艰巨的任务&#xff0c;因为你必须确保你有足够的时间来开发你的第一个小程序。如果你决定使用小程序&#xff0c;那就有很多事情要做。创建一个小程序可能是一件非常耗时的事情…

无线蓝牙耳机推荐有哪些?八大无线蓝牙耳机排行

在近几年蓝牙耳机的飞速发展&#xff0c;我们对于音乐和通讯的需求也越来越高。传统的耳机和听筒虽然能够满足我们基本的听觉需求&#xff0c;但是它们也带来了一些问题&#xff0c;比如&#xff1a;长时间佩戴会导致耳朵疲劳、引起耳道炎等。针对这些问题&#xff0c;蓝牙耳机…

解决Dbeaver连接一段时间不操作后断开的问题

1、首先右键数据库连接点击【编辑连接】 2、点击【初始化】将【连接保持】改成60s&#xff0c;这样数据库就不会自己断开了

动态组件和异步组件

动态组件 相关api <!-- 失活的组件将会被缓存&#xff01;--> <keep-alive include"Tab1,Tab2"><component :is"currentTabComponent"></component> </keep-alive>component属性 is“全局注册或局部注册的组件名” keep…

Windows安装Pytorch3d

Windows安装Pytorch3d 1.前提&#xff1a; 安装Visual Studio 2019 【我记得必须是2017-2019之间的版本&#xff0c;我一开始用的是2022的版本就安装不了】网址pytorch和pytorch3d、cuda和NVIDIA CUB版本需要相互对应 pytorch和pytorch3d版本对应关系如下&#xff1a;https:…

springcloud-Nacos处理高并发的注册

添加服务 第一 次判断 提供性能&#xff1a;避免多个线程同时在等 synchronzied 释放 第二次 判断 &#xff1a; 别的线程可能已经将实例加入了 serviceMap() 注意这里还有个ConcurrentSkipListMap 有利于提高读写性能。 所以内层的Map 是个ConcurrentSkipLlistMap&#xff…

Binder对象的流转(系统服务的调用过程、AIDL的使用过程)

零、Binder的传递 Android系统中&#xff0c;存在大量的 IPC 交互&#xff0c;同时也使用了大量的 Binder&#xff0c;那么Binder是怎么在各进程中进行对象的传递&#xff1f; 一、调用系统服务时&#xff0c;Binder的传递 回忆一下&#xff0c;Android系统的启动流程&#x…

看一图而思全云

>> 前言 << 我在看财经十一人吴俊宇老师撰写的《阿里云计划在12月内独立上市》时&#xff0c;看到了一张全球及中国IT支出结构图。就是下图这张图&#xff0c;盯着这张图&#xff0c;我看到了星辰大海&#xff0c;也想到了广阔天地大有可为。 但只看这个图不够过瘾…

1.7C++流插入运算符重载

C流插入运算符重载 在 C 中&#xff0c;流插入运算符&#xff08;<<&#xff09;用于输出数据到流中的运算符&#xff0c;流插入运算符可以被重载&#xff0c;使得程序员可以自定义输出对象的方式。 重载流插入运算符的一般形式如下&#xff1a; 其中&#xff0c;T 是…

Vue中如何进行瀑布流布局与图片加载优化

Vue中如何进行瀑布流布局与图片加载优化 瀑布流布局是一种常用的网页布局方式&#xff0c;它可以让页面看起来更加有趣和美观。在Vue.js中&#xff0c;我们可以使用第三方插件或者自己编写组件来实现瀑布流布局。同时&#xff0c;为了优化图片加载的性能&#xff0c;我们还可以…

部署minio分布式测试环境

准备了4台虚拟机作为minio分布式节点服务器。 操作系统为TencentOS3.1(相当于CentOS8) 选择从官网下载minio安装包,minio-20230602231726.0.0.x86_64.rpm 安装命令如下: rpm -ivh minio-20230602231726.0.0.x86_64.rpm 安装完毕,minio命令将会放在/usr/local/bin下。…

推动开源行业高质量发展|2023开放原子全球开源峰会圆满落幕

6 月 13 日&#xff0c;由 2023 全球数字经济大会组委会主办&#xff0c;开放原子开源基金会、北京市经济和信息化局、北京经济技术开发区管理委员会承办的 2023 开放原子全球开源峰会在北京顺利落下帷幕。本次峰会以“开源赋能&#xff0c;普惠未来”为主题&#xff0c;设置了…

多任务学习用于多模态生物数据分析

目前的生物技术可以同时测量来自同一细胞的多种模态数据&#xff08;例如RNA、DNA可及性和蛋白质&#xff09;。这需要结合不同的分析任务&#xff08;如多模态整合和跨模态分析&#xff09;来全面理解这些数据&#xff0c;推断基因调控如何驱动生物多样性。然而&#xff0c;目…

佩戴舒适的蓝牙耳机评测,值得入手的蓝牙耳机推荐

蓝牙耳机越来越少受年轻人的欢迎&#xff0c;蓝牙耳机品牌也非常多&#xff0c;很多朋友在选择时&#xff0c;不知道选哪款好&#xff0c;下面是小编整理的蓝牙耳机品牌排行榜前十名&#xff0c;大家可以参考参考。 一、南卡OE蓝牙耳机 参考价格&#xff1a;469 舒适性最高的…

4个维度搞懂Nacos注册中心

现如今市面上注册中心的轮子很多&#xff0c;我实际使用过的就有三款&#xff1a;Eureka、Gsched、Nacos&#xff0c;由于当前参与 Nacos 集群的维护和开发工作&#xff0c;期间也参与了 Nacos 社区的一些开发和 Bug Fix 工作&#xff0c;过程中对 Nacos 原理有了一定的积累&am…

专为AI大模型打造的GPU加速器MI300X

作者 I 刘博 54岁神秘女掌门&#xff1a;年薪4亿元&#xff0c;一代女强人 AMD与苏姿丰的名字如今深深绑定在一起。 1969年&#xff0c;苏姿丰出生在中国台湾&#xff1b;同一年&#xff0c;日后与她渊源颇深AMD在硅谷创立。谁也不会想到&#xff0c;她将在几十年后拯救AMD。 …