有了前面的铺垫我们终于可以开始实现我们的主角 ActiveThread 了,虽然它登场的有点晚。
这个系列的开头我们提到 ActiveThread
有两个鲜明的特点:
- 它是线程,可以运行,代表一个单独的执行上下文(execution unit/context)
- 每个
ActiveThread
内部运行一个 message-loop,方便持续的执行我们提交的任务
这两点和 Android 原生提供的 HandlerThread
是一模一样的。
不过和 HandlerThread
不同,我们这里不打算采用继承 Thread
的方式,而是采用 composition。原因之一是我个人非常反感传统的 OO 继承手法;并且 Java 8 开始正式支持 lambda 之后,不用继承我们的工作也可以做得很好。
不适用继承同时有个好处,我们可以只暴露我们需要的接口,避免误用(比如经典的用 run()
而不是 start()
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| import java.util.concurrent.locks.Condition;
public class ActiveThread { private Thread _thread; private MessageLoop _loop; private AutoReentrantLock _loopInitLock = new AutoReentrantLock(); private Condition _loopInited = _loopInitLock.newCondition();
public ActiveThread(String name) { _thread = new Thread(this::runThread, name); }
public void start() { _thread.start(); }
public boolean quit() { if (getMessageLoop() == null) { return false; }
_loop.quit();
return true; }
public long getId() { return _thread.getId(); }
public String getName() { return _thread.getName(); }
public boolean isAlive() { return _thread.isAlive(); }
public MessageLoop getMessageLoop() { if (!_thread.isAlive()) { return null; }
try (AutoCloseableLock lock = _loopInitLock.lockAsAuto()) { while (_thread.isAlive() && _loop == null) { try { _loopInited.await(); } catch (InterruptedException ignored) { } } }
return _loop; }
private void runThread() { MessageLoop.prepare();
try (AutoCloseableLock lock = _loopInitLock.lockAsAuto()) { _loop = MessageLoop.current(); _loopInited.signalAll(); }
MessageLoop.loop();
MessageLoop.reset(); } }
|
通过调用 start()
开始运行线程后,我们准备的 runThread()
函数会被执行,这个函数的作用就是
- 准备消息循环
- 执行消息循环
一旦这个函数返回,就代表着我们的线程即将狗带。
外界通过 getMessageLoop()
来获取线程的消息循环,这点和 HandlerThread.getLooper()
类似。
并且,因为线程初始化时可能会和用户在外部尝试获取 message-loop 产生 race-condition,所以这里我们使用一个 condition-variable 来做同步,保证对 message-loop 的访问是线程安全的。
另外,一旦 _loop
被设置,后续的函数对它只有读操作,不会引发 race-condition。
注:对初始化 latency 不是很敏感的程序,例如网络服务程序,在这种环境下可以直接 block 线程的 start()
函数,保证 start()
返回后 message-loop 已经是正常工作的。
Misc
如果线程还需要暴露一些操作,比如设置 exception handler 啥的,可以直接通过 ActiveThread 的函数做一层转发即可。
整个基础设施而言,最复杂的是如何设计 MessageLoop
(以及内部的 MessagePump
)。有了这些基础工作,上面的活都是水到渠成。