前言
之前介绍了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
来源:稀土掘金