Android登录拦截的场景-基于线程池的实现

前言

之前介绍了AOP的方式、方法池的方式、消息通知的方式。这一期我们使用线程池来实现一个登陆拦截的功能。

线程池怎么能实现这样的逻辑呢?或者说线程池如何能实现这么一个逻辑呢?

  • 方式一:我们使用while的无限轮询方法,当满足条件用户登录成功,那么我们就放开轮询执行下面的跳转目标的任务。

  • 方式二:我们使用线程的wait和notify的方法,控制让线程停止与运行。当条件满足继续运行的时候从而执行正确的目标任务。

  • 方式三:我们基于线程的FutureTask与Callable来获取到结果,触发正确的目标任务。

这里我个人不是很推荐方式一,因为线程一直运行,还是相比其他方式更耗费资源一点,所以这里举例方式二和方式三,如果有需求,大家理解了就很轻易的自己去实现方式一了。

一、线程的 wait / notify

由于是线程池实现的,这里以Java代码为例

/**
 * 登录拦截的线程管理
 */
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()) {
            //已经登录
            mHandler.post(nextRunnable);
            return;
        }

        //如果没有登录-先去登录页面
        mHandler.post(loginRunnable);

        singleThreadExecutor.execute(() -> {

            try {
                YYLogUtils.w("开始运行-停止");

                synchronized (singleThreadExecutor) {
                    singleThreadExecutor.wait();

                    YYLogUtils.w("等待notifyAll完成了,继续执行");

                    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();
        }
    }

}

使用的时候,写两个Runnable就行了,一个是目标任务,一个是登录任务

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

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

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

当我们登录完成之后需要通知唤醒线程

    fun doLogin() {
        showStateLoading()

        CommUtils.getHandler().postDelayed({
            showStateSuccess()

            SP().putString(Constants.KEY_TOKEN, "abc")

            LoginInterceptThreadManager.get().loginFinished()

            finish()

            }, 500)

        }

代码是很简单的线程的wait与notify,我们看看实现的效果。

二、FutureTask与Callable

FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,之后可以在外部通过FutureTask的get方法异步获取执行结果

关于FutureTask的使用与原理分析,可以看看这个博客。由于是Java库中的东西,这里我做过多的赘述。

我们可以这么简单的理解:通过线程池运行 FutureTask 任务,然后通过 Callable 来回调到 done() 方法,在内部再通过 get() 获取到回调的值。

然后我们理一下思路,怎么实现我们需要的逻辑,我们可以在 Callable 的方法中通过 Handler 的 wait 的形式来阻塞线程,通过Handler 的 notify 来唤醒,从而继续Callable,回调给FutureTask,先直接看代码。


/**
 * 登录拦截的线程管理
 */
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()) {
            //已经登录
            mHandler.post(nextRunnable);
            return;
        }

        //如果没有登录-先去登录页面
        mHandler.post(loginRunnable);

        //开启一个线程
        singleThreadExecutor.execute(new FutureTask<Boolean>(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                YYLogUtils.w("call()");

                synchronized (mHandler) {
                    mHandler.wait();
                    YYLogUtils.w("等待notifyAll,继续执行");
                    return LoginManager.isLogin();
                }
            }
        }) {
            @Override
            protected void done() {

                YYLogUtils.w("done()");

                try {
                    final boolean result = get();
                    mHandler.removeCallbacksAndMessages(null);
                    if (result) {
                        mHandler.post(nextRunnable);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

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

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

    }

}

通过Handler的wait与notify来实现类似的效果,后续的使用的方式和上面的方式是一样的,效果也和上面的方式一致。

总结

其实基于线程实现的这一个功能,本质就是让线程停住,等我登录逻辑完成了,在唤醒线程,继续往下走,从而实现跳转到个人信息的页面。

这里的两种方式,实现的原理有差异,但是思路是一个意思,只是一个是线程自己下场了,线程给wait、notify了,另一个是更加委婉一点,间接的使用 Handler 的 wait 和 notify 的方式。从而间距的实现等待与唤醒的逻辑。

而文章开头介绍的第一种方式,使用线程 while 的方式我就不做演示,也是比较简单,使用一个flag来判断是否需要结束循环,这种方式线程一直运行,相对耗费资源,不是很推荐。

文本的代码都已贴出,如果有兴趣想运行项目看效果,源码在此

前文有说到,这是一系列的文章集,实现登录拦截再执行的方案有很多,我分为不同的思路来实现。每一种思路又可能几种不同的实现,很多都是之前的一些技术点的实战应用,关于登录拦截后续还有其他的思路的文章,还请期待。

我早没有存稿了,一天一更的压力太大了,平常的开发任务页蛮紧的,文章比较匆忙,如果有错字或代码错误的地方,还望多多包涵,评论区指出即可。

如果你对此思路觉得有更优的办法,或者觉得当前方案有不妥之处,都可以评论区交流一下。

作者:newki
链接:https://juejin.cn/post/7133387325681172488
来源:稀土掘金

猜你喜欢

转载自blog.csdn.net/m0_64420071/article/details/126423958