Android实战 -> 使用Interceptor+Lock实现无缝刷新Token

前言

哈喽各位我又来了,相信大家在做APP的时候肯定会遇到用户Token即将过期或者已经过期的情况,那么这个时候后端都返回相应的Code提示我们要求刷新Token处理。

那么今天这篇文章就给大家提供一个思路。

开工

技术点
  • Interceptor -> 拦截器
  • ReentrantLock -> 重入锁
实现思路
  • 通过TokenInterceptor获取Response解析请求结果验证是否Token过期
  • 监控到Token已过期后阻塞当前线程,调用刷新Token接口并使用Lock锁
  • 并发的请求也监控到了Token过期后,先校验Lock是否已锁,已锁等待,未锁步骤2
  • Token刷新成功后各线程携带新的Token创建Request重新请求

总结:4个并发线程接口,谁抢到了Lock锁谁去刷新Token,其他三个线程阻塞等待

实现代码
kotlin 复制代码
private fun handle(name: String) {
    Log.d(TAG, "handle 【Start】 called with: name = $name")
    try {
        if (!mLock.isLocked) {
            this.mLock.lock() // 加锁
            Log.d(TAG, "handle 【Start  Refresh】 called with: name = $name")
            Thread.sleep(5000) // 此处应为刷新Token请求
            Log.d(TAG, "handle 【End Refresh】 called with: name = $name")
            this.mLock.unlock() // 释放锁
        } else {
            Log.d(TAG, "handle 【Wait Refresh】 called with: name = $name")
            while (true) { // 阻塞等待
                if (!mLock.isLocked) { // 查询锁状态
                    Log.d(TAG, "handle 【OK Refresh】 called with: name = $name")
                    break
                }
            }
        }
    } finally {
        if (mLock.isLocked) {
            this.mLock.unlock()
        }
    }
    Log.d(TAG, "handle 【End】 called with: name = $name")
}

如上述代码,抢到Lock锁的线程去刷新Token,其余线程等待结果。

模拟测试
arduino 复制代码
// 此处模拟并发请求
this.findViewById<View>(R.id.btnGo).setOnClickListener {
    thread {
        handle("线程1")
    }
    thread {
        handle("线程2")
    }
    thread {
        handle("线程3")
    }
}
输出日志

如图,线程2抢到了Lock锁,线程1、3则进入了等待状态

如图,线程2刷新Token成功后释放了锁,线程1、3监听到了锁被释放则进入重新请求逻辑

完整代码
kotlin 复制代码
class MainAc : AppCompatActivity() {

    private val TAG = "zzz"

    private val mLock by lazy { ReentrantLock() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        super.setContentView(R.layout.ac_main)

        this.findViewById<View>(R.id.btnGo).setOnClickListener {
            thread {
                handle("线程1")
            }
            thread {
                handle("线程2")
            }
            thread {
                handle("线程3")
            }
        }
    }


    private fun handle(name: String) {
        Log.d(TAG, "handle 【Start】 called with: name = $name")
        try {
            if (!mLock.isLocked) {
                this.mLock.lock()
                Log.d(TAG, "handle 【Start  Refresh】 called with: name = $name")
                Thread.sleep(5000)
                Log.d(TAG, "handle 【End Refresh】 called with: name = $name")
                this.mLock.unlock()
            } else {
                Log.d(TAG, "handle 【Wait Refresh】 called with: name = $name")
                while (true) {
                    if (!mLock.isLocked) {
                        Log.d(TAG, "handle 【Go Refresh】 called with: name = $name")
                        break
                    }
                }
            }
        } finally {
            if (mLock.isLocked) {
                this.mLock.unlock()
            }
        }
        Log.d(TAG, "handle 【End】 called with: name = $name")
    }
}
End

到这里就结束了,简单吧,希望可以帮到在座的小伙伴们。当然如果有更好的实现方式或方案也希望各位在评论区留言讨论,我秒回复哦~ Bye

相关推荐
minimoon_jojo21 小时前
Ant Design 树形表格渲染原理
前端
paopaokaka_luck21 小时前
基于springboot3+vue3的音乐推荐系统(协同过滤算法、Echarts图形化分析)
前端·echarts
八号当铺1 天前
使用 Figma Agent Kit:插件 + MCP + 还原 Skill,打通本地设计协作
前端·人工智能·ai编程
一心只读圣贤书1 天前
AI 辅助前端国际化实践:从文案梳理到多语言资源治理
前端
无责任此方_修行中1 天前
搓了一个国产大模型与 AI Agent 比价工具
前端·后端·ai编程
szephyr1 天前
腾讯云 ADP 智能体的 Skills 配置变更后没有走灰度,直接全量替换出了问题怎么定位?
java·前端·腾讯云·腾讯云adp
FungLeo1 天前
Flutter 吸顶分组列表实战:语义桶分组 + 点击头平滑滚动
android·flutter
夏雪coding1 天前
nginx SPA 回落把 CSS 变成了 HTML:一个返回 200 却让样式失效的坑
前端·nginx
光影少年1 天前
React Native网络、存储与原生能力
前端·react native·react.js
bonibabi1 天前
基于 CopilotKit + Java SSE 构建 AI Agent 的前端实践指南
前端·agent