由HandlerThread引起的输入法线程的思考

今天在网上看了HandlerThread的文章,又想起自己设计的输入法引擎的是否有哪些缺陷和修补的地方。HandlerThread就不介绍了,直接贴个链接吧https://www.jianshu.com/p/de2ff82b37b3

我自己设计的输入法引擎也是工作在work thread中,这个work thread在运行的过程中,创建looper实例和handler实例,该handler会通过接口publish出去。UI层的按键消息是通过该handler传递给work thread。

但是当时项目上线后,发现了很多crash,当时分析后,觉得是输入法UI键盘弹出来后,引擎所在的work thread还没有开始运行,那么当UI发送按键消息时,会得到关于work thread的handler的空指针异常。于是用空对象模式来解决这个问题,就是在work thread还未运行时,先把该handler引用一个空对象(NullLogicHandler),这样就避免了空指针异常,同时把该handler设置成volatile(使它被重新赋值时保证它的可视性)。代码如下所示

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
public class LogicThread extends Thread {
private Looper looper;
private volatile LogicHandler handler = new NullLogicHandler();
private PyLogicStateContext pyLogicStateContext;

public LogicThread(PyLogicStateContext pyLogicStateContext) {
this.pyLogicStateContext = pyLogicStateContext;
}

public LogicHandler getHandler() {
return handler;
}

@Override
public void run() {
Looper.prepare();

looper = Looper.myLooper();
handler = new LogicHandler(looper, pyLogicStateContext);
Looper.loop();
}

public void exit() {
if (looper != null) {
looper.quit();
looper = null;
}
}
}

但是我花了时间设计了这个类,发现还存在问题,理论上UI最早发送的消息有可能会丢失(因为此时handler还在指向NullLogicHandler)。为什么不直接用HandlerThread呢?HandlerThread还提供了锁操作,可以让main thread和work thread保持线程同步。如果用HandlerThread,main thread中可能会存在锁和wait操作,来等待HandlerThread中的looper实例化。临时想到的main thread中的一个设计模型,如下

1
2
3
4
// 首先前提保证handler Thread已经被start了
handlerThread.start();
//创建 work thread 的handler
Handler handler = new Handler(handlerThread.getLooper());

该方法的一个瑕疵是理论上需要在main thread中进行等待(不提倡在main thread中有等待操作。。。),但是如果做一个普通app的话,这种程度上的解决方案应该是可以了。

追求极致的话,避免任何在main thread中做等待,就在想另外一个方案,大致思考方向是,当检测到work thread还未运行或者looper还未创建,那么就把main thread中的按键消息都临时add到一个线程安全的队列中,该队列的message会在work thread一切都准备好后被它消费掉。