Android登錄攔截場景-探討多種實(shí)現(xiàn)方式

前言

本文是基于之前的系列文章做的一個合集,精簡之后整理為一篇長文供大家參考耘成。合集的入口在此。合集內(nèi)部有每種方案的詳細(xì)使用手冊,大家可以對照本文參考使用。

登錄攔截與放行是大部分App開發(fā)都會遇到的一個場景,如果你的App有游客模式堡称,但是部分高級功能需要登錄之后才能使用。

那么我們就需要在用戶點(diǎn)擊這個操作的時候校驗(yàn)是否登錄艺演,當(dāng)?shù)卿浲瓿芍笤偬D(zhuǎn)到指定的頁面或彈窗却紧。如果這些入口很多的話,那么我們就需要到處寫這些邏輯胎撤。比較初級的用法是使用消息總線晓殊,當(dāng)?shù)卿浲瓿芍蟀l(fā)送對應(yīng)key消息,然后去完成對應(yīng)key的事件伤提。

有沒有一種更簡單的方式巫俺,集中統(tǒng)一方便的管理登錄攔截再放行這一個場景。

下面我們一起來看一看具體的方案肿男。

一介汹、方法池方案

本質(zhì)就是把你要攔截執(zhí)行的方法作為一個對象,存入到一個方法池列表中舶沛,使用完之后再自動釋放掉嘹承。(需要注意生命周期,當(dāng)頁面Destory的時候要主動釋放)

先定義方法對象

public abstract class IFunction {

    public String functionName;

    public IFunction(String functionName) {

        this.functionName = functionName;
    }

    protected abstract void function();

}

方法池:

public class FunctionManager {

    private static FunctionManager functionManager;

    private static HashMap<String, IFunction> mFunctionMap;

    public FunctionManager() {
        mFunctionMap = new HashMap<>();

    }

    public static FunctionManager get() {
        if (functionManager == null) {
            functionManager = new FunctionManager();
        }
        return functionManager;
    }

    /**
     * 添加方法
     */
    public FunctionManager addFunction(IFunction function) {
        if (mFunctionMap != null) {
            mFunctionMap.put(function.functionName, function);
        }
        return this;
    }

    /**
     * 執(zhí)行方法
     */
    public void invokeFunction(String key) {
        if (TextUtils.isEmpty(key)) {
            return;
        }
        if (mFunctionMap != null) {
            IFunction function = mFunctionMap.get(key);

            if (function != null) {
                function.function();
                //用完移除掉
                removeFunction(key);
            } else {
                try {
                    throw new RuntimeException("function not found");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用之后移除相關(guān)的緩存
     */
    public void removeFunction(String key) {
        if (mFunctionMap != null) {
            mFunctionMap.remove(key);
        }
    }

}

使用的時候也是非常簡單

    private fun checkLogin() {
        if (SP().getString(Constants.KEY_TOKEN, "").checkEmpty()) {

            FunctionManager.get().addFunction(object : IFunction("gotoProfilePage") {
                override fun function() {
                    gotoProfilePage()
                }
            })

            gotoLoginPage()

        } else {

            gotoProfilePage()
        }
    }

登錄完成之后如庭,我們需要手動調(diào)用

    //方法池的方式
    FunctionManager.get().invokeFunction("gotoProfilePage")

這樣就可以觸發(fā)回調(diào)完成登錄攔截的功能了赶撰。

如果想對游客的校驗(yàn)也做一個封裝,也可以在 FunctionManager 中定義好柱彻,可以自由擴(kuò)展。

二餐胀、消息回調(diào)方案

其本質(zhì)是通過消息總線實(shí)現(xiàn)哟楷,通過管理類發(fā)送消息,接收消息否灾,通過回調(diào)的方式去執(zhí)行攔截的方法卖擅。相比前者,他的好處是不需要我們處理生命周期墨技。

我們指定好統(tǒng)一的消息key之后惩阶,都通過這個key來處理登錄完成的邏輯


public class FunctionManager {

    private static FunctionManager functionManager;

    private static HashMap<String, Function> mFunctionMap;

    public FunctionManager() {
        mFunctionMap = new HashMap<>();

    }

    public static FunctionManager get() {
        if (functionManager == null) {
            functionManager = new FunctionManager();
        }
        return functionManager;
    }

    public void addLoginCallback(LifecycleOwner owner, ILoginCallback callback) {
        LiveEventBus.get("login", Boolean.class).observe(owner, aBoolean -> {
            if (aBoolean != null && aBoolean) {
                callback.callback();
            }
        });
    }

    public interface ILoginCallback {
        void callback();
    }

    public void finishLogin() {
        LiveEventBus.get("login").post(true);
    }
}

 FunctionManager.get().addLoginCallback(this) {
            gotoProfilePage()
        }

登錄完成之后,我們需要手動調(diào)用

    //方法池的方式
    FunctionManager.get().finishLogin()

這樣就可以觸發(fā)回調(diào)完成登錄攔截的功能了扣汪。

和方法池的方式又異曲同工之妙断楷。

三、Intent的方案

其實(shí)不使用一些容器崭别,我們原始的使用Intent也是可以實(shí)現(xiàn)邏輯的冬筒。

原理是通過登錄成功之后startActivity啟動自己的頁面恐锣,然后通過 onNewIntent 拿到對應(yīng)的操作意圖去執(zhí)行對應(yīng)的操作。

只是需要我們把原始的意圖封裝到啟動自己的Intent中舞痰。


    fun switchPage3() {
            f (!LoginManager.isLogin()) {
            val intent = Intent(mActivity, Demo3Activity::class.java)
            intent.addCategory(switch_tab3)

            gotoLoginPage(intent)

        } else {
                switchFragment(3)
        }
    }

    //把原始意圖當(dāng)參數(shù)傳遞
    fun gotoLoginPage(targetIntent: Intent) {
        val intent = Intent(mActivity, LoginDemoActivity::class.java)
        intent.putExtra("targetIntent", targetIntent)
        startActivity(intent)
    }

    //通過這樣的方式可以拿到攜帶的數(shù)據(jù)
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        YYLogUtils.w("收到newintent:" + intent.toString())
        val categories = intent.categories

        when (categories.take(1)[0]) {
            switch_tab1 -> {
                switchFragment(1)
            }
            switch_tab2 -> {
                switchFragment(2)
            }
            switch_tab3 -> {
                switchFragment(3)
            }
        }

    }

那么在Login頁面登錄完成之后再啟動當(dāng)前頁面即可把攜帶的數(shù)據(jù)傳遞回來土榴,通過newIntent就可以做對應(yīng)的操作。

四响牛、動態(tài)代理+Hook的方案

如果說Intent的方案還需要我們手動的處理跳轉(zhuǎn)玷禽,那么此方案就是升級版,自動的攔截跳轉(zhuǎn)呀打,之后的放行方案我們還是通過 Intent 與 onNewIntent 的回調(diào)來處理矢赁。

難點(diǎn)就是如何使用Hook代替Activity的啟動。

public class DynamicProxyUtils {

    //修改啟動模式
    public static void hookAms() {
        try {

            Field singletonField;
            Class<?> iActivityManager;
            // 1聚磺,獲取Instrumentation中調(diào)用startActivity(,intent,)方法的對象
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                // 10.0以上是ActivityTaskManager中的IActivityTaskManagerSingleton
                Class<?> activityTaskManagerClass = Class.forName("android.app.ActivityTaskManager");
                singletonField = activityTaskManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
                iActivityManager = Class.forName("android.app.IActivityTaskManager");
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // 8.0,9.0在ActivityManager類中IActivityManagerSingleton
                Class activityManagerClass = ActivityManager.class;
                singletonField = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
                iActivityManager = Class.forName("android.app.IActivityManager");
            } else {
                // 8.0以下在ActivityManagerNative類中 gDefault
                Class<?> activityManagerNative = Class.forName("android.app.ActivityManagerNative");
                singletonField = activityManagerNative.getDeclaredField("gDefault");
                iActivityManager = Class.forName("android.app.IActivityManager");
            }
            singletonField.setAccessible(true);
            Object singleton = singletonField.get(null);

            // 2坯台,獲取Singleton中的mInstance,也就是要代理的對象
            Class<?> singletonClass = Class.forName("android.util.Singleton");
            Field mInstanceField = singletonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);

            Method getMethod = singletonClass.getDeclaredMethod("get");
            Object mInstance = getMethod.invoke(singleton);
            if (mInstance == null) {
                return;
            }

            //開始動態(tài)代理
            Object proxy = Proxy.newProxyInstance(
                    Thread.currentThread().getContextClassLoader(),
                    new Class[]{iActivityManager},
                    new AmsHookBinderInvocationHandler(mInstance));

            //現(xiàn)在替換掉這個對象
            mInstanceField.set(singleton, proxy);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //動態(tài)代理執(zhí)行類
    public static class AmsHookBinderInvocationHandler implements InvocationHandler {

        private Object obj;

        public AmsHookBinderInvocationHandler(Object rawIActivityManager) {
            obj = rawIActivityManager;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            if ("startActivity".equals(method.getName())) {

                Intent raw;
                int index = 0;
                for (int i = 0; i < args.length; i++) {
                    if (args[i] instanceof Intent) {
                        index = i;
                        break;
                    }
                }

                //原始意圖
                raw = (Intent) args[index];
                YYLogUtils.w("原始意圖:" + raw);

                //設(shè)置新的Intent-直接制定LoginActivity
                Intent newIntent = new Intent();
                String targetPackage = "com.guadou.kt_demo";
                ComponentName componentName = new ComponentName(targetPackage, LoginDemoActivity.class.getName());
                newIntent.setComponent(componentName);

                YYLogUtils.w("改變了Activity啟動");

                args[index] = newIntent;

                YYLogUtils.w("攔截activity的啟動成功" + " --->");

                return method.invoke(obj, args);

            }

            //如果不是攔截的startActivity方法瘫寝,就直接放行
            return method.invoke(obj, args);
        }

    }
}

使用的時候我們需要啟動代理蜒蕾,在跳轉(zhuǎn)頁面的時候就會自動攔截了。

    mBtnProfile.click {

        //啟動動態(tài)代理
         DynamicProxyUtils.hookAms()

        gotoActivity<ProfileDemoActivity>()
    }

之后的邏輯和上面的Intent方案是一樣的回調(diào)處理焕阿,走 onNewIntent 里面處理咪啡。

目前的Hook只兼容到Android12。還沒有看13的源碼不知道有沒有變動暮屡。并且此方案只能適用于頁面的跳轉(zhuǎn)撤摸,有些場景比如切換Tab、ViewPager的情況下褒纲,是無法實(shí)現(xiàn)攔截的准夷。

如果不想全部的頁面都攔截,大家也可以自行實(shí)現(xiàn)白名單的管理莺掠,只攔截部分的頁面衫嵌。

但相對其他方案來說其實(shí)不是很好用,這樣的自動感覺還不如全手動的Intent靈活彻秆。

五楔绞、Java線程方案

相對其他的方案,此方案的思路就比較清奇唇兑,利用線程的等待與恢復(fù)來實(shí)現(xiàn)酒朵,當(dāng)我們跳轉(zhuǎn)到登錄頁面的時候我們讓線程等待,然后等待登錄完成之后我們再恢復(fù)等待扎附。

/**
 * 登錄攔截的線程管理
 */
public class LoginInterceptThreadManager  {

    private static LoginInterceptThreadManager threadManager;

    private static final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    private static final Handler mHandler = new Handler();

    private LoginInterceptThreadManager() {
    }

    public static LoginInterceptThreadManager get() {
        if (threadManager == null) {
            threadManager = new LoginInterceptThreadManager();
        }

        return threadManager;
    }

    /**
     * 檢查是否需要登錄
     */
    public void checkLogin(Runnable nextRunnable, Runnable loginRunnable) {

        if (LoginManager.isLogin()) {
            //已經(jīng)登錄
            mHandler.post(nextRunnable);
            return;
        }

        //如果沒有登錄-先去登錄頁面
        mHandler.post(loginRunnable);

        singleThreadExecutor.execute(() -> {

            try {
                YYLogUtils.w("開始運(yùn)行-停止");

                synchronized (singleThreadExecutor) {
                    singleThreadExecutor.wait();

                    YYLogUtils.w("等待notifyAll完成了,繼續(xù)執(zhí)行");

                    if (LoginManager.isLogin()) {
                        mHandler.post(nextRunnable);
                    }
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

    }

    public void loginFinished() {
        if (mHandler == null) return;
        if (singleThreadExecutor == null) return;

        synchronized (singleThreadExecutor) {
            singleThreadExecutor.notifyAll();
        }
    }

}

使用的時候也簡單

    private fun checkLogin() {
        LoginInterceptThreadManager.get().checkLogin( {
            gotoProfilePage()
        }, {
            gotoLoginPage()
        })
    }

    private fun gotoLoginPage() {
        gotoActivity<LoginDemoActivity>()
    }

    private fun gotoProfilePage() {
        gotoActivity<ProfileDemoActivity>()
    }

登錄完成之后蔫耽,我們需要手動調(diào)用

    //方法池的方式
    oginInterceptThreadManager.get().loginFinished()

這樣就可以觸發(fā)回調(diào)完成登錄攔截的功能了。

六留夜、Kotlin協(xié)程方案

既然線程都可以针肥,沒道理協(xié)程不能使用這樣的方案饼记,協(xié)程也可以使用等待恢復(fù)的方案,還能使用協(xié)程通信的方案慰枕,開啟兩個協(xié)程具则,然后當(dāng)?shù)卿浲瓿芍笕ネㄖ渲械慕邮諈f(xié)程去繼續(xù)執(zhí)行。

class LoginInterceptCoroutinesManager private constructor() : DefaultLifecycleObserver, CoroutineScope by MainScope() {

    companion object {
        private var instance: LoginInterceptCoroutinesManager? = null
            get() {
                if (field == null) {
                    field = LoginInterceptCoroutinesManager()
                }
                return field
            }

        fun get(): LoginInterceptCoroutinesManager {
            return instance!!
        }
    }

    private lateinit var mCancellableContinuation: CancellableContinuation<Boolean>

    fun checkLogin(loginAction: () -> Unit, nextAction: () -> Unit) {

        launch {

            if (LoginManager.isLogin()) {
                nextAction()
                return@launch
            }

            loginAction()

            val isLogin = suspendCancellableCoroutine<Boolean> {

                mCancellableContinuation = it

                YYLogUtils.w("暫停協(xié)程具帮,等待喚醒")
            }

            YYLogUtils.w("已經(jīng)恢復(fù)協(xié)程博肋,繼續(xù)執(zhí)行")
            if (isLogin) {
                nextAction()
            }

        }
    }

    fun loginFinished() {

        if (!this@LoginInterceptCoroutinesManager::mCancellableContinuation.isInitialized) return

        if (mCancellableContinuation.isCancelled) return

        mCancellableContinuation.resume(LoginManager.isLogin(), null)

    }

    override fun onDestroy(owner: LifecycleOwner) {
        YYLogUtils.w("LoginInterceptCoroutinesManager - onDestroy")

        mCancellableContinuation.cancel()
        cancel()
    }

}

使用也比較簡單

       //協(xié)程的方式
        mBtnProfile2.click {
            LoginInterceptCoroutinesManager.get().checkLogin(loginAction = {
                gotoLoginPage()
            }, nextAction = {
                gotoProfilePage()
            })

        }

登錄完成之后,我們需要手動調(diào)用

    //方法池的方式
    oginInterceptThreadManager.get().loginFinished()

這樣就可以觸發(fā)回調(diào)完成登錄攔截的功能了蜂厅。

協(xié)程另一種方案就是通知的方式:

class LoginInterceptCoroutinesManager private constructor() : DefaultLifecycleObserver, CoroutineScope by MainScope() {

    companion object {
        private var instance: LoginInterceptCoroutinesManager? = null
            get() {
                if (field == null) {
                    field = LoginInterceptCoroutinesManager()
                }
                return field
            }

        fun get(): LoginInterceptCoroutinesManager {
            return instance!!
        }
    }

    private val channel = Channel<Boolean>()

    fun checkLogin(loginAction: () -> Unit, nextAction: () -> Unit) {

        launch {

            if (LoginManager.isLogin()) {
                nextAction()
                return@launch
            }

            loginAction()

            val isLogin = channel.receive()

            YYLogUtils.w("收到消息:" + isLogin)

            if (isLogin) {
                nextAction()
            }
        }
    }

    fun loginFinished() {

        launch {

            async {
                YYLogUtils.w("發(fā)送消息:" + LoginManager.isLogin())
                channel.send(LoginManager.isLogin())
            }

        }
    }

    override fun onDestroy(owner: LifecycleOwner) {
        cancel()
    }

}

使用起來和暫头朔玻恢復(fù)的方案是一樣樣的。

七掘猿、Aop切面方案

除了這些方案之外病游,網(wǎng)上比較流行的就是面向切面AOP的方案。

需要我們集成 AspectJ 框架來實(shí)現(xiàn)稠通。

使用的時候就需要定義一個自定義的注解衬衬,然后圍繞這個注解做一些操作。

//不需要回調(diào)的處理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}

除了注解的類

@Aspect
public class LoginAspect {

    @Pointcut("@annotation(com.guadou.kt_demo.demo.demo3_bottomtabbar_fragment.aop.Login)")
    public void Login() {
    }

    @Pointcut("@annotation(com.guadou.kt_demo.demo.demo3_bottomtabbar_fragment.aop.LoginCallback)")
    public void LoginCallback() {
    }

    //帶回調(diào)的注解處理
    @Around("LoginCallback()")
    public void loginCallbackJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        YYLogUtils.w("走進(jìn)AOP方法-LoginCallback()");
        Signature signature = joinPoint.getSignature();

        if (!(signature instanceof MethodSignature)){
            throw new RuntimeException("該注解只能用于方法上");
        }

        LoginCallback loginCallback = ((MethodSignature) signature).getMethod().getAnnotation(LoginCallback.class);
        if (loginCallback == null) return;

        //判斷當(dāng)前是否已經(jīng)登錄
        if (LoginManager.isLogin()) {
            joinPoint.proceed();

        } else {
            LifecycleOwner lifecycleOwner = (LifecycleOwner) joinPoint.getTarget();

            LiveEventBus.get("login").observe(lifecycleOwner, new Observer<Object>() {
                @Override
                public void onChanged(Object integer) {
                    try {
                        joinPoint.proceed();
                        LiveEventBus.get("login").removeObserver(this);

                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                        LiveEventBus.get("login").removeObserver(this);
                    }
                }
            });

            LoginManager.gotoLoginPage();
        }
    }

    //不帶回調(diào)的注解處理
    @Around("Login()")
    public void loginJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        YYLogUtils.w("走進(jìn)AOP方法-Login()");
        Signature signature = joinPoint.getSignature();

        if (!(signature instanceof MethodSignature)){
            throw new RuntimeException("該注解只能用于方法上");
        }

        Login login = ((MethodSignature) signature).getMethod().getAnnotation(Login.class);
        if (login == null) return;

        //判斷當(dāng)前是否已經(jīng)登錄
        if (LoginManager.isLogin()) {
            joinPoint.proceed();
        } else {
            //如果未登錄改橘,去登錄頁面
            LoginManager.gotoLoginPage();
        }

    }
}

定義一個工具類來定義一些固定的方法:

object LoginManager {

    @JvmStatic
    fun isLogin(): Boolean {
        val token = SP().getString(Constants.KEY_TOKEN, "")
        YYLogUtils.w("LoginManager-token:$token")
        val checkEmpty = token.checkEmpty()
        return !checkEmpty
    }

    @JvmStatic
    fun gotoLoginPage() {
        commContext().gotoActivity<LoginDemoActivity>()
    }
}

到這里我們就能使用AOP來攔截了滋尉。我們把需要攔截的方法使用我們的自定義注解來標(biāo)記。然后我們的處理器就會對這個注解做一些圍繞的操作飞主。

    override fun init() {

        mBtnCleanToken.click {
            SP().remove(Constants.KEY_TOKEN)
            toast("清除成功")
        }

        mBtnProfile.click {

           //不帶回調(diào)的登錄方式
           gotoProfilePage2()
        }

    }

    @Login
    private fun gotoProfilePage2() {
        gotoActivity<ProfileDemoActivity>()
    }

可以看到內(nèi)部也是通過消息總線來執(zhí)行繼續(xù)操作的邏輯的狮惜,我們需要在登錄完成之后發(fā)送這個通知才行。

八碌识、攔截器的方案

最后一種方案是基于責(zé)任鏈模式的改版碾篡,自定義攔截器實(shí)現(xiàn)的,和默認(rèn)的責(zé)任鏈?zhǔn)怯行┎町惖姆げ汀F渲袥]有用到參數(shù)的傳遞耽梅。

原理是我們定義2層攔截,一個是校驗(yàn)登錄胖烛,一個是執(zhí)行邏輯。當(dāng)我們校驗(yàn)登錄不通過的時候就會跳轉(zhuǎn)到登錄頁面诅迷,當(dāng)?shù)卿浲瓿芍笈宸覀兝^續(xù)攔截器就會走到執(zhí)行邏輯。間接的完成一個登錄攔截的功能罢杉。

攔截器的定義

object LoginInterceptChain {

    private var index: Int = 0

    private val interceptors by lazy(LazyThreadSafetyMode.NONE) {
        ArrayList<Interceptor>(2)
    }

    //默認(rèn)初始化Login的攔截器
    private val loginIntercept = LoginInterceptor()

    // 執(zhí)行攔截器趟畏。
    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 {
        //默認(rèn)添加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()
    }

}

校驗(yàn)登錄的攔截器:

/**
 * 判斷是否登錄的攔截器
 */
class LoginInterceptor : BaseLoginInterceptImpl() {

    override fun intercept(chain: LoginInterceptChain) {
        super.intercept(chain)

        if (LoginManager.isLogin()) {
            //如果已經(jīng)登錄 -> 放行, 轉(zhuǎn)交給下一個攔截器
            chain.process()
        } else {
            //如果未登錄 -> 去登錄頁面
            LoginDemoActivity.startInstance()
        }
    }

    fun loginfinished() {
        //如果登錄完成滩租,調(diào)用方法放行到下一個攔截器
        mChain?.process()
    }
}

繼續(xù)執(zhí)行的攔截器:

/**
 * 登錄完成下一步的攔截器
 */
class LoginNextInterceptor(private val action: () -> Unit) : BaseLoginInterceptImpl() {

    override fun intercept(chain: LoginInterceptChain) {
        super.intercept(chain)

        if (LoginManager.isLogin()) {
            //如果已經(jīng)登錄執(zhí)行當(dāng)前的任務(wù)
            action()
        }

        mChain?.process()
    }

}

使用的時候我們使用攔截器管理即可

    private fun checkLogin() {
        LoginInterceptChain.addInterceptor(LoginNextInterceptor {
            gotoProfilePage()
        }).process()
    }

登錄完成之后記得手動放行哦

    //攔截器放行
    LoginInterceptChain.loginFinished()

這樣就完成了登錄攔截的功能了赋秀。

總結(jié)

本文是一個總綱或者說是總結(jié)利朵,這里的幾種方法我都只是簡單的介紹了一下,具體的使用可以看看單獨(dú)的文章猎莲,每一篇具體使用的方式之前都已經(jīng)出了對應(yīng)的文章绍弟,并附帶了Demo,有興趣的朋友可以前往查看著洼。

總的來說實(shí)現(xiàn)這種方式推薦大家使用簡單易于理解和集成使用的方式樟遣。例如方法池,消息通知回調(diào)身笤,線程協(xié)程的方案豹悬,自定義攔截的方案其實(shí)都是不錯的,大家自己按需選擇即可液荸。

除開一些集成困難瞻佛,有兼容性的一些方案之外,其他的這些方案都是可以用的了娇钱,剩下的我們需要考慮的就是伤柄,此方案是否有更大的內(nèi)存開銷,是否有內(nèi)存泄露風(fēng)險(xiǎn)忍弛,需要處理頁面意外關(guān)閉的情況嗎响迂?有沒有降級或兜底的方案?有沒有崩潰的風(fēng)險(xiǎn)细疚?有沒有重復(fù)調(diào)用的風(fēng)險(xiǎn)蔗彤?等等等等。

本文也只是基于Demo的實(shí)現(xiàn)疯兼,如果正式在生產(chǎn)上面使用的話然遏,大家可以自行擴(kuò)展一下它的健壯性。

本文全部代碼均以開源吧彪,源碼在此待侵。大家可以點(diǎn)個Star關(guān)注一波,有問題我會及時更新姨裸。

好了秧倾,本期內(nèi)容如有錯漏的地方,希望同學(xué)們可以指出交流傀缩。如果有更好的方法那先,也歡迎大家評論區(qū)討論。

如果感覺本文對你有一點(diǎn)點(diǎn)的啟發(fā)赡艰,還望你能點(diǎn)贊支持一下,你的支持是我最大的動力售淡。

作者:newki

鏈接:https://juejin.cn/post/7143040409558581262

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子揖闸,更是在濱河造成了極大的恐慌揍堕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汤纸,死亡現(xiàn)場離奇詭異衩茸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹲嚣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門递瑰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人隙畜,你說我怎么就攤上這事抖部。” “怎么了议惰?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵慎颗,是天一觀的道長。 經(jīng)常有香客問我言询,道長俯萎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任运杭,我火速辦了婚禮夫啊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辆憔。我一直安慰自己撇眯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布虱咧。 她就那樣靜靜地躺著熊榛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腕巡。 梳的紋絲不亂的頭發(fā)上玄坦,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音绘沉,去河邊找鬼煎楣。 笑死,一個胖子當(dāng)著我的面吹牛车伞,可吹牛的內(nèi)容都是我干的择懂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼帖世,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起日矫,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狡赐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栏豺,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年窃诉,在試婚紗的時候發(fā)現(xiàn)自己被綠了杨耙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡飘痛,死狀恐怖珊膜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宣脉,我是刑警寧澤车柠,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站塑猖,受9級特大地震影響竹祷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羊苟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一塑陵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜡励,春花似錦令花、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至占遥,卻和暖如春俯抖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓦胎。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工芬萍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搔啊。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓柬祠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親负芋。 傳聞我的和親對象是個殘疾皇子漫蛔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容