如何创建Looper线程,然后立即发送消息?

Tho*_*mas 73 multithreading android handler looper

我有一个工作线程,它位于后台,处理消息.像这样的东西:

class Worker extends Thread {

    public volatile Handler handler; // actually private, of course

    public void run() {
        Looper.prepare();
        mHandler = new Handler() { // the Handler hooks up to the current Thread
            public boolean handleMessage(Message msg) {
                // ...
            }
        };
        Looper.loop();
    }
}
Run Code Online (Sandbox Code Playgroud)

从主线程(UI线程,不重要)我想做这样的事情:

Worker worker = new Worker();
worker.start();
worker.handler.sendMessage(...);
Run Code Online (Sandbox Code Playgroud)

麻烦的是,这为我设定了一个漂亮的竞争条件:在worker.handler阅读时,没有办法确定工作线程已经分配给了这个领域!

我不能简单地HandlerWorker构造函数创建,因为构造函数在主线程上运行,因此Handler它将自己与错误的线程相关联.

这似乎不太常见.我可以提出几个解决方法,所有这些都是丑陋的:

  1. 像这样的东西:

    class Worker extends Thread {
    
        public volatile Handler handler; // actually private, of course
    
        public void run() {
            Looper.prepare();
            mHandler = new Handler() { // the Handler hooks up to the current Thread
                public boolean handleMessage(Message msg) {
                    // ...
                }
            };
            notifyAll(); // <- ADDED
            Looper.loop();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    并从主线程:

    Worker worker = new Worker();
    worker.start();
    worker.wait(); // <- ADDED
    worker.handler.sendMessage(...);
    
    Run Code Online (Sandbox Code Playgroud)

    但这也不可靠:如果notifyAll()发生在之前wait(),那么我们永远不会被唤醒!

  2. 将一个首字母传递MessageWorker构造函数,让run()方法发布它.一个临时解决方案,不适用于多条消息,或者如果我们不想立即发送,但很快就会发送.

  3. 忙着等待直到handler现场不再null.是的,万不得已......

我想创建一个HandlerMessageQueue代表的Worker线程,但是这似乎并不可能.什么是最优雅的方式?

Tho*_*mas 61

最终的解决方案(减去错误检查),感谢CommonsWare:

class Worker extends HandlerThread {

    // ...

    public synchronized void waitUntilReady() {
        d_handler = new Handler(getLooper(), d_messageHandler);
    }

}
Run Code Online (Sandbox Code Playgroud)

并从主线程:

Worker worker = new Worker();
worker.start();
worker.waitUntilReady(); // <- ADDED
worker.handler.sendMessage(...);
Run Code Online (Sandbox Code Playgroud)

这要归功于HandlerThread.getLooper()在初始化looper之前块的语义.


顺便说一句,这与我上面的解决方案#1类似,因为HandlerThread它的实现大致如下(得到爱开源):

public void run() {
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Looper.loop();
}

public Looper getLooper() {
    synchronized (this) {
        while (mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}
Run Code Online (Sandbox Code Playgroud)

关键的区别在于它不会检查工作线程是否正在运行,而是它实际上已经创建了一个looper; 并且这样做的方法是将活套存放在私人领域.太好了!

  • @Snicolas你不能,因为处理程序被限制在创建它的线程.在HandlerThread的构造函数中初始化它将导致主线程中的处理程序或创建HandlerThread的位置.如果你想使用Handler构造函数和Handler的looper作为参数(waituntilready中使用的那个),我很确定它会导致死锁. (4认同)
  • @Thomas`d_handler`的主要目的是什么?`d_messageHandler`处理消息,对吧?但是你使用`work.handler`发送meesage (2认同)