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


76

我有一个工作线程位于后台,用于处理消息。像这样:

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();
    }
}

从主线程(UI线程,没关系),我想做这样的事情:

Worker worker = new Worker();
worker.start();
worker.handler.sendMessage(...);

麻烦在于,这使我处于良好的竞争状态:在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();
        }
    }
    

    从主线程:

    Worker worker = new Worker();
    worker.start();
    worker.wait(); // <- ADDED
    worker.handler.sendMessage(...);
    

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

  2. 将缩写传递MessageWorker的构造函数,并让run()方法发布它。临时解决方案不适用于多封邮件,或者如果我们不想立即发送,而是在不久之后发送。

  3. 忙于等待,直到handler田野不再null。是的,不得已...

我想创建HandlerMessageQueue代表Worker线程,但这似乎是不可能的。最优雅的方法是什么?


12
您没有使用任何特殊原因HandlerThread吗?
CommonsWare,

2
@CommonsWare:嗯,不知道它存在。在文档中没有交叉引用。它的getLooper()方法将阻塞,直到有一个为止Looper,然后我们可以new Handler(worker.getLooper()) 从主线程使用来初始化Handler。这样可以解决问题,对吗?
托马斯(Thomas)

我认同。太太,我自己用的不多,所以我可能会缺少一些东西。
CommonsWare,

3
@CommonsWare:它解决了问题。现在,如果您将其作为答案发布,我将在其旁边放一个绿色大勾号;)
Thomas

9
实际上,我认为最好自己回答一下,以说明如何HandlerThread适应您的Worker模式。至少,您会比我更好地解释它,因为这是您的问题以及解决方案的实现-我只是指出了一个帮助程序类来解决该问题。
CommonsWare

Answers:


64

最终的解决方案(减去错误检查),这要归功于CommonsWare:

class Worker extends HandlerThread {

    // ...

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

}

从主线程:

Worker worker = new Worker();
worker.start();
worker.waitUntilReady(); // <- ADDED
worker.handler.sendMessage(...);

这要归功于语义的HandlerThread.getLooper()阻塞,直到循环程序初始化为止。


顺便说一句,这类似于我上面的解决方案#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;
}

关键区别在于它不检查工作线程是否正在运行,而是实际上创建了一个循环程序。这样做的方法是将弯针存储在私有字段中。真好!


1
谢谢你 我只是在指出必须在worker.start()之后调用waitUntilReady ()。回顾起来听起来似乎很明显,但是花了我一些时间才能弄明白在得到空指针异常时我出错了。
fedepaol

4
@Snicolas不能,因为处理程序已绑定到创建它的线程。在HandlerThread的构造函数中对其进行初始化将导致在主线程或创建HandlerThread的任何位置创建处理程序。如果要使用带有Handler循环器的Handler构造函数作为参数(在waituntilready中使用的那个),我很确定这会导致死锁。
fedepaol 2012

1
好的,那是一个好点。Thx发布。因此,为了防止处理程序被滥用,您可以将处理程序方法包装到可以充当外观的HandlerThread中,并检查处理程序初始化后将每个调用委托给处理程序。那将是封装它并防止遗忘waitUntilReady的更好方法。
尼古拉斯

2
@Thomas的主要目的是d_handler什么?d_messageHandler 处理消息,对不对?但是,你正在使用发送meesagework.handler
woyaru

1
这非常有趣,但是如果您提供有关d_handler和d_messageHandler是什么以及如何使用它们的一些注释,它将更加有用。
RenniePet 2015年

1

看一下源代码 HandlerThread

@Override
     public void run() {
         mTid = Process.myTid();
         Looper.prepare();
         synchronized (this) {
             mLooper = Looper.myLooper();
             notifyAll();
         }
         Process.setThreadPriority(mPriority);
         onLooperPrepared();
         Looper.loop();
         mTid = -1;
     }

基本上,如果要在worker中扩展Thread并实现自己的Looper,则您的主线程类应扩展worker并在此处设置处理程序。


1

这是我的解决方案:MainActivity:

//Other Code

 mCountDownLatch = new CountDownLatch(1);
        mainApp = this;
        WorkerThread workerThread = new WorkerThread(mCountDownLatch);
        workerThread.start();
        try {
            mCountDownLatch.await();
            Log.i("MsgToWorkerThread", "Worker Thread is up and running. We can send message to it now...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Toast.makeText(this, "Trial run...", Toast.LENGTH_LONG).show();
        Message msg = workerThread.workerThreadHandler.obtainMessage();
        workerThread.workerThreadHandler.sendMessage(msg);

WorkerThread类:

public class WorkerThread extends Thread{

    public Handler workerThreadHandler;
    CountDownLatch mLatch;

    public WorkerThread(CountDownLatch latch){

        mLatch = latch;
    }


    public void run() {
        Looper.prepare();
        workerThreadHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {

                Log.i("MsgToWorkerThread", "Message received from UI thread...");
                        MainActivity.getMainApp().runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.getMainApp().getApplicationContext(), "Message received in worker thread from UI thread", Toast.LENGTH_LONG).show();
                                //Log.i("MsgToWorkerThread", "Message received from UI thread...");
                            }
                        });

            }

        };
        Log.i("MsgToWorkerThread", "Worker thread ready...");
        mLatch.countDown();
        Looper.loop();
    }
}

0
    class WorkerThread extends Thread {
            private Exchanger<Void> mStartExchanger = new Exchanger<Void>();
            private Handler mHandler;
            public Handler getHandler() {
                    return mHandler;
            }
            @Override
            public void run() {
                    Looper.prepare();
                    mHandler = new Handler();
                    try {
                            mStartExchanger.exchange(null);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    Looper.loop();
            }

            @Override
            public synchronized void start() {
                    super.start();
                    try {
                            mStartExchanger.exchange(null);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
            }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.