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

时间:2011-01-29 17:13:31

标签: android multithreading 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();
    }
}

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

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

麻烦的是,这为我设定了一个漂亮的竞争条件:在读取worker.handler时,无法确定工作线程是否已分配给该字段!

我不能简单地从Handler的构造函数创建Worker,因为构造函数在主线程上运行,因此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. 将初始Message传递给Worker的构造函数,并使用run()方法发布它。一个临时解决方案不适用于多条消息,或者我们不想立即发送,但很快就会发送。

  3. 忙碌等待handler字段不再是null。是的,不得已......

  4. 我想代表Handler线程创建MessageQueueWorker,但这似乎不可能。什么是最优雅的方式?

3 个答案:

答案 0 :(得分:60)

最终解决方案(减去错误检查),感谢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()的语义,它会在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;
}

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

答案 1 :(得分: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();
    }
}

答案 2 :(得分: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();
                    }
            }
    }