Как создать тему Looper, а затем немедленно отправить ей сообщение? - PullRequest
73 голосов
/ 29 января 2011

У меня есть рабочий поток, который работает в фоновом режиме и обрабатывает сообщения.Примерно так:

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

Из основного потока (поток пользовательского интерфейса, не то, что это имеет значение) Я хотел бы сделать что-то вроде этого:

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.Да, последнее средство ...

Я хотел бы создать Handler и MessageQueue от имени потока Worker, но это не представляется возможным,Какой самый элегантный выход из этого?

Ответы [ 4 ]

60 голосов
/ 31 января 2011

Возможное решение (без проверки ошибок), благодаря 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 голос
/ 24 ноября 2015

Это мои решения: 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();
    }
}
1 голос
/ 16 августа 2015

взгляните на исходный код HandlerThread

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

По сути, если вы расширяете Thread в работнике и реализуете свой собственный Looper, то ваш основной класс потока должен расширять работника и устанавливать там ваш обработчик.

0 голосов
/ 09 июля 2015
    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();
                    }
            }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...