Android登录拦截的场景-基于拦截器模式实现
theme: juejin highlight: a11y-dark
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情
登录拦截系列: * 登录拦截-AOP的实现 * 登录拦截-方法池和消息回调的实现 * 登录拦截-线程的实现 * 登录拦截-协程的实现 * 登录拦截-Intent的实现 * 登录拦截-动态代理Hook的实现 * 登录拦截-拦截器模式的实现
前言
前面的文章讲了一些APP登录拦截再执行的功能实现的几种方案,登录拦截,登录拦截,唉?我们能不能用拦截器模式实现?
关于拦截的实战应用,之前讲过两篇不同的类型,一种是值传递的方式 拦截实现Log的打印与保存。另一种是非值传递的方式 拦截弹窗的展示。不了解的可以阅读一下我之前文章。
如何实现拦截器拦截登录逻辑?我们整理一下思路:
- 我们拦截登录的场景是通过是否已经登录的状态来判断是否拦截,是外部变量的判断,我们无需值传递的方式。
- 我们需要添加固定一个拦截器判断是否登录,是否允许通行。
- 我们需要动态的添加一个拦截器,用于执行跳转个人中心的逻辑,并且在执行完毕之后移除拦截器。
- 我们在定义一个管理类,定义统一的入口,添加拦截器,移除拦截,继续登录拦截等操作。
下面我们就开始吧,尝试拦截器的定义与实现。
一、默认拦截器
这是一套固定的代码,我们先把拦截器基类拿过来定义。
kotlin
interface Interceptor {
fun intercept(chain: InterceptChain)
}
```kotlin abstract class BaseInterceptImpl : Interceptor {
protected var mChain: InterceptChain? = null
@CallSuper
override fun intercept(chain: InterceptChain) {
mChain = chain
}
} ```
```kotlin
class InterceptChain private constructor(
// 弹窗的时候可能需要Activity/Fragment环境。
val activity: FragmentActivity? = null,
val fragment: Fragment? = null,
private var interceptors: MutableList
@JvmStatic
fun create(count: Int = 0): Builder {
return Builder(count)
}
}
private var index: Int = 0
// 执行拦截器。
fun process() {
interceptors ?: return
when (index) {
in interceptors!!.indices -> {
val interceptor = interceptors!![index]
index++
interceptor.intercept(this)
}
interceptors!!.size -> {
clearAllInterceptors()
}
}
}
private fun clearAllInterceptors() {
interceptors?.clear()
interceptors = null
}
// 构建者模式。
open class Builder(private val count: Int = 0) {
private val interceptors by lazy(LazyThreadSafetyMode.NONE) {
ArrayList<Interceptor>(count)
}
private var activity: FragmentActivity? = null
private var fragment: Fragment? = null
// 添加一个拦截器。
fun addInterceptor(interceptor: Interceptor): Builder {
if (!interceptors.contains(interceptor)) {
interceptors.add(interceptor)
}
return this
}
// 关联Fragment。
fun attach(fragment: Fragment): Builder {
this.fragment = fragment
return this
}
// 关联Activity。
fun attach(activity: FragmentActivity): Builder {
this.activity = activity
return this
}
fun build(): InterceptChain {
return InterceptChain(activity, fragment, interceptors)
}
}
} ```
我们把之前的代码拷贝过来修改一下,由于我们不是弹窗逻辑,不依赖于Activity/Fragment,所以我们可以更加的精简代码,修改如下:
kotlin
interface Interceptor {
fun intercept(chain: LoginInterceptChain)
}
```kotlin abstract class BaseLoginInterceptImpl : Interceptor {
protected var mChain: LoginInterceptChain? = null
@CallSuper
override fun intercept(chain: LoginInterceptChain) {
mChain = chain
}
} ```
二、拦截器管理类与默认拦截器
由于没有那么多的参数,我们就无需使用构建者模式,直接设置管理类定义为单例对象,直接修改即可。
```kotlin object LoginInterceptChain {
private var index: Int = 0
private val interceptors by lazy(LazyThreadSafetyMode.NONE) {
ArrayList<Interceptor>(2)
}
//默认初始化Login的拦截器
private val loginIntercept = LoginInterceptor()
// 执行拦截器。
fun process() {
if (interceptors.isEmpty()) return
when (index) {
in interceptors.indices -> {
val interceptor = interceptors[index]
index++
interceptor.intercept(this)
}
interceptors.size -> {
clearAllInterceptors()
}
}
}
// 添加一个拦截器。
fun addInterceptor(interceptor: Interceptor): LoginInterceptChain {
//默认添加Login判断的拦截器
if (!interceptors.contains(loginIntercept)) {
interceptors.add(loginIntercept)
}
if (!interceptors.contains(interceptor)) {
interceptors.add(interceptor)
}
return this
}
//放行登录判断拦截器
fun loginFinished() {
if (interceptors.contains(loginIntercept) && interceptors.size > 1) {
loginIntercept.loginfinished()
}
}
//清除全部的拦截器
private fun clearAllInterceptors() {
index = 0
interceptors.clear()
}
} ```
主要是要定义一个CheckLogin的拦截器 ```kotlin /* * 判断是否登录的拦截器 / class LoginInterceptor : BaseLoginInterceptImpl() {
override fun intercept(chain: LoginInterceptChain) {
super.intercept(chain)
if (LoginManager.isLogin()) {
//如果已经登录 -> 放行, 转交给下一个拦截器
chain.process()
} else {
//如果未登录 -> 去登录页面
LoginDemoActivity.startInstance()
}
}
fun loginfinished() {
//如果登录完成,调用方法放行到下一个拦截器
mChain?.process()
}
} ```
具体的代码已经在拦截器管理类中提供了: ```kotlin // 添加一个拦截器。 fun addInterceptor(interceptor: Interceptor): LoginInterceptChain { //默认添加Login判断的拦截器 if (!interceptors.contains(loginIntercept)) { interceptors.add(loginIntercept) }
if (!interceptors.contains(interceptor)) {
interceptors.add(interceptor)
}
return this
}
//放行登录判断拦截器
fun loginFinished() {
if (interceptors.contains(loginIntercept) && interceptors.size > 1) {
loginIntercept.loginfinished()
}
}
```
检查登录状态的拦截器我们需要固定在拦截器链的第一个位置,当我们登录完成之后我们通过管理类间接的调用这个拦截器放行。
由于我们这个场景是可以多次调用的,所以我们每次拦截使用完毕之后我们需要清除全部的拦截器,或者也可以只清除最后一个拦截。
kotlin
private fun clearAllInterceptors() {
index = 0
interceptors.clear()
}
一定要注意,由于是重复使用的拦截器,记得index要归位,不然无法第二次执行。
二、自定义拦截实现
之前我们定义了检查登录状态的拦截器,并固定在拦截器链的第一个位置,然后我们需要自定义一个拦截器,用于登录完成之后的继续执行。
如果是Kotlin语言,我们通过构造方法传入一个高阶函数回调即可, ```kotlin /* * 登录完成下一步的拦截器 / class LoginNextInterceptor(private val action: () -> Unit) : BaseLoginInterceptImpl() {
override fun intercept(chain: LoginInterceptChain) {
super.intercept(chain)
if (LoginManager.isLogin()) {
//如果已经登录执行当前的任务
action()
}
mChain?.process()
}
} ```
如果是Java语言开发,我们直接定义为abstract的方法,使用的时候可以重写抽象方法即可使用,有需要的话大家使用Java语言自己实现。 ```kotlin /* * 登录完成下一步的拦截器 / abstract class LoginNextInterceptor() : BaseLoginInterceptImpl() {
override fun intercept(chain: LoginInterceptChain) {
super.intercept(chain)
if (LoginManager.isLogin()) {
//如果已经登录执行当前的任务
runAction()
}
mChain?.process()
}
abstract fun runAction()
} ```
那么我们就能使用了。这里使用的是Kotlin构造实现高阶函数的方式: ```kotlin //拦截器的方式 mBtnProfile.click { checkLogin() }
private fun checkLogin() {
LoginInterceptChain.addInterceptor(LoginNextInterceptor {
gotoProfilePage()
}).process()
}
```
我们在登录完成的时候调用 loginfinished 方法即可
```kotlin fun doLogin() { showStateLoading()
CommUtils.getHandler().postDelayed({
showStateSuccess()
SP().putString(Constants.KEY_TOKEN, "abc")
//拦截器放行
LoginInterceptChain.loginFinished()
finish()
}, 500)
}
```
效果:
总结
拦截器模式真的是非常好用的设计模式,在Android的开发过程中用到的地方很多,可以非常方便的实现各种各样的效果。
关于登录拦截再执行的这一个场景,我总结了几种方式,到今天这一篇就算完结了,大家更喜欢哪一种方式呢?
啥?你问我使用的哪一种方案? 其实我个人觉得AOP和Intent太麻烦,动态代理担心以后会有兼容性问题,并且不能覆盖全部场景,协程和线程的实现对内存的开销相对来说会稍微大一点,我个人还是比较喜欢使用简单,方便集成,内存开销小的一些方式,所以个人比较推荐方法池,通知,拦截器,这三种方案。
其实几种方案都能实现这个场景,大家按需选择即可。
当然了,其实还有其他的一些的方法我并没有穷举出来,比如有人基于ARouter来实现拦截,有人基于OkHttp的拦截方案实现,这些基于一些框架实现的我并没有讲,第一是因为实现方式都是类似拦截器的方式,第二是很多同学在开发中并不会使用这些框架。
大家自己目前用的又是哪一种方案呢?大家更中意哪一种呢?欢迎评论区讨论哦。当然了,我个人能力也有限,如果有其他类型的更多更好的方式,各位高工也可以评论区指出哦。
好了,我本人如有讲解不到位或错漏的地方,希望同学们可以指出交流。
如果感觉本文对你有一点点点的启发,还望你能点赞
支持一下,你的支持是我最大的动力。
Ok,这一期就此完结。
- Android操作文件也太难了趴,File vs DocumentFile 以及 DocumentsProvider vs FileProvider 的异同
- findViewById不香吗?为什么要把简单的问题复杂化?为什么要用DataBinding?
- Android自定义View绘制进阶-水波浪温度刻度表
- Android自定义ViewGroup布局进阶,完整的九宫格实现
- 记录仿抖音的视频播放并缓存预加载视频的效果实现
- Kotlin对象的懒加载方式?by lazy 与 lateinit 的异同
- 定位都得集成第三方?Android原生定位服务LocationManager不行吗?
- 还用第三方库管理状态栏吗?Android关于状态栏管理的几种方案实现!
- 下载需要集成第三方?Android原生下载服务DownloadManager不行吗?
- Android阴影实现的几种方案-自定义圆角ViewGroup加入阴影效果
- 操作Android窗口的几种方式?WindowInsets与其兼容库的使用与踩坑
- Android软键盘与布局的协调-不同的效果与实现方案的探讨
- ViewPager2:ViewPager都能自动嵌套滚动了,我不行?我麻了!该怎么做?
- Android软键盘的监听与高度控制的几种方案及常用效果
- 圆角升级啦,来手把手一起实现自定义ViewGroup的各种圆角与背景
- Android导航栏的处理-HostStatusLayout加入底部的导航栏适配
- 一次搞懂怎么设置圆角图片,ImageView的各种圆角设置
- 一看就会 Android框架DataBinding的使用与封装
- 别滥用FileProvider了,Android中FileProvider的各种场景应用
- Android登录拦截的场景-基于拦截器模式实现