Android登录拦截的场景-基于拦截器模式实现

语言: CN / TW / HK

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? ) { companion object {

    @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)

    }

```

效果:

test_05.gif

总结

拦截器模式真的是非常好用的设计模式,在Android的开发过程中用到的地方很多,可以非常方便的实现各种各样的效果。

关于登录拦截再执行的这一个场景,我总结了几种方式,到今天这一篇就算完结了,大家更喜欢哪一种方式呢?

啥?你问我使用的哪一种方案? 其实我个人觉得AOP和Intent太麻烦,动态代理担心以后会有兼容性问题,并且不能覆盖全部场景,协程和线程的实现对内存的开销相对来说会稍微大一点,我个人还是比较喜欢使用简单,方便集成,内存开销小的一些方式,所以个人比较推荐方法池,通知,拦截器,这三种方案。

其实几种方案都能实现这个场景,大家按需选择即可。

当然了,其实还有其他的一些的方法我并没有穷举出来,比如有人基于ARouter来实现拦截,有人基于OkHttp的拦截方案实现,这些基于一些框架实现的我并没有讲,第一是因为实现方式都是类似拦截器的方式,第二是很多同学在开发中并不会使用这些框架。

大家自己目前用的又是哪一种方案呢?大家更中意哪一种呢?欢迎评论区讨论哦。当然了,我个人能力也有限,如果有其他类型的更多更好的方式,各位高工也可以评论区指出哦。

好了,我本人如有讲解不到位或错漏的地方,希望同学们可以指出交流。

如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。

Ok,这一期就此完结。

「其他文章」