由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(使它被重新赋值时保证它的可视性)。代码如下所示

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中的一个设计模型,如下

        // 首先前提保证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一切都准备好后被它消费掉。

回主页