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登入攔截的場景-基於攔截器模式實現