Обработчики, MessageQueue, Looper, все ли они работают в потоке пользовательского интерфейса?
Я пытаюсь обвести голову потоками, и я знаю, что я могу использовать Handler
для отправки сообщений /runnables в MessageQueue
, который, в свою очередь, подбирается Looper
и отправляется обратно к Обработчику для обработки.
Если я отправляю обработчик в своей деятельности, работают ли теги Activity
, Handler
, MessageQueue
и Looper
все в потоке пользовательского интерфейса? Если нет, может кто-нибудь объяснить, как все это объединяется?:)
Ответы
Ответ 1
Короткий ответ: все они работают в одном потоке. Если они созданы из обратного вызова жизненного цикла Activity
, все они запускаются в основном потоке пользовательского интерфейса.
Длинный ответ:
В потоке может быть Looper
, который содержит MessageQueue
.. Чтобы использовать этот объект, вам нужно создать Looper
в текущем потоке, вызвав ( static) Looper.prepare()
, а затем запустите цикл, вызвав (также статический) Looper.loop()
. Они являются статическими, поскольку предполагается, что только один Looper
для потока.
Вызов loop()
обычно не возвращает в течение некоторого времени, но продолжает принимать сообщения ( "задачи", "команды" или все, что вы хотите называть) из MessageQueue
и обрабатывает их индивидуально (например, путем вызова Runnable
, содержащегося в сообщении). Когда в очереди нет сообщений, поток блокируется до появления новых сообщений. Чтобы остановить Looper
, вы должны называть quit()
на нем (что, вероятно, не останавливает цикл сразу, а скорее устанавливает закрытый флаг, который периодически проверяется из цикла, сигнализируя об этом, чтобы остановить).
Однако вы не можете напрямую добавлять сообщения в очередь. Вместо этого вы регистрируете MessageQueue.IdleHandler
для ожидания обратного вызова queueIdle()
, в котором вы можете решить, хотите ли вы чего-то или нет. Все обработчики вызываются поочередно. (So "очередь" на самом деле не очередь, а вместо этого набор обратных вызовов, которые будут называться регулярно.)
Примечание относительно предыдущего абзаца: Это я действительно догадался. Я не мог найти документацию, но это имело бы смысл.
update: см. комментарий ahcox и его ответ. p >
Поскольку это большая работа, структура предоставляет класс Handler
для упрощения. Когда вы создаете экземпляр Handler
, он (по умолчанию) привязан к Looper
, уже прикрепленному к текущему потоку. (Handler
знает, к чему привязать Looper
, потому что мы ранее назовем prepare()
, который, вероятно, сохранил ссылку на Looper
в ThreadLocal
.)
С помощью Handler
вы можете просто вызвать post()
, чтобы "поместить сообщение в очередь сообщений потока" (так сказать). Handler
позаботится обо всех материалах обратного вызова IdleHandler
и убедитесь, что вы выполнили свой Runnable
. (Он также может проверить правильность времени, если вы отправили с задержкой.)
Просто для того, чтобы быть ясным: единственный способ на самом деле сделать поток циклов сделать что-то, чтобы отправить сообщение в него цикл. Это действует до тех пор, пока вы не назовете quit() на петлере.
Относительно потока пользовательского интерфейса Android: В какой-то момент (возможно, до того, как будут созданы какие-либо действия и т.д.), инфраструктура настроила Looper
(содержащую MessageQueue
) и запустила ее. С этого момента все, что происходит в потоке пользовательского интерфейса, проходит через этот цикл. Это включает в себя управление жизненным циклом деятельности и т.д. Все обратные вызовы, которые вы переопределяете (onCreate()
, onDestroy()
...), по крайней мере, опосредованно отправляются из этого цикла. Вы можете увидеть это, например, в трассировке стека исключения. (Вы можете попробовать, просто напишите int a = 1 / 0;
где-нибудь в onCreate()
...)
Надеюсь, это имеет смысл. Извините, что ранее неясно.
Ответ 2
Взаимосвязь с вопросом "как все это вместе". Как писал user634618, петлератор берет поток, основной поток пользовательского интерфейса в случае
application main Looper
.
-
Looper.loop()
выводит сообщения из очереди сообщений. Каждое сообщение имеет ссылку на связанного обработчика, что он должен быть возвращен (целевой член).
- Внутри
Looper.loop()
для каждого сообщения, полученного из очереди:
-
loop()
вызывает public void Handler.dispatchMessage(Message msg)
с помощью обработчика, который хранится в сообщении в качестве его целевого члена.
- Если сообщение содержит элемент обратного вызова Runnable, который запускается.
- Иначе, если обработчик имеет общий набор обратных вызовов, который запускается.
- Else, Обработчик
handleMessage()
вызывается с сообщением в качестве аргумента. (Обратите внимание, что если вы выполняете обработчик подкласса как AsyncTask, вы можете переопределить handleMessage()
, как он есть.)
В вопросе о том, что все сотрудничающие объекты находятся в одном и том же потоке пользовательского интерфейса,
a Handler
должен быть создан в том же потоке, что и Looper
, который он отправит
сообщений.
Его конструктор будет искать текущий Looper
и хранить его как члена, связывая
Handler
к этому Looper
.
Он также будет ссылаться на эту очередь сообщений Looper
непосредственно в своем собственном элементе.
Handler
можно использовать для отправки работы в Looper
из любого потока, но это
идентификация очередей сообщений направляет работу, выполняемую в потоке Looper
.
Когда мы запускаем некоторый код в другом потоке и хотим отправить Runnable для выполнения в потоке пользовательского интерфейса, мы можем сделать это следующим образом:
// h is a Handler that we constructed on the UI thread.
public void run_on_ui_thread(final Handler h, final Runnable r)
{
// Associate a Message with our Handler and set the Message's
// callback member to our Runnable:
final Message message = Message.obtain(h, r);
// The target is the Handler, so this asks our Handler to put
// the Message in its message queue, which is the exact same
// message queue associated with the Looper on the thread on
// which the Handler was created:
message.sendToTarget();
}
Ответ 3
Я пытаюсь реализовать этот интерфейс сам, чтобы понять концепцию.
По простоте просто используйте интерфейс по необходимости.
Вот мой тестовый код:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class TestLooper {
public static void main(String[] args) {
UIThread thread = new UIThread();
thread.start();
Handler mHandler = new Handler(thread.looper);
new WorkThread(mHandler, "out thread").run();
}
}
class Looper {
private BlockingQueue<Message> message_list = new LinkedBlockingQueue<Message>();
public void loop() {
try {
while (!Thread.interrupted()) {
Message m = message_list.take();
m.exeute();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void insertMessage(Message msg) {
message_list.add(msg);
}
}
class Message {
String data;
Handler handler;
public Message(Handler handler) {
this.handler = handler;
}
public void setData(String data) {
this.data = data;
}
public void exeute() {
handler.handleMessage(this);
}
}
class Handler {
Looper looper;
public Handler(Looper looper) {
this.looper = looper;
}
public void dispatchMessage(Message msg) {
System.out.println("Handler dispatchMessage" + Thread.currentThread());
looper.insertMessage(msg);
}
public Message obtainMessage() {
return new Message(this);
}
public void handleMessage(Message m) {
System.out.println("handleMessage:" + m.data + Thread.currentThread());
}
}
class WorkThread extends Thread {
Handler handler;
String tag;
public WorkThread(Handler handler, String tag) {
this.handler = handler;
this.tag = tag;
}
public void run() {
System.out.println("WorkThread run" + Thread.currentThread());
Message m = handler.obtainMessage();
m.setData("message " + tag);
handler.dispatchMessage(m);
}
}
class UIThread extends Thread {
public Looper looper = new Looper();
public void run() {
//create handler in ui thread
Handler mHandler = new Handler(looper);
new WorkThread(mHandler, "inter thread").run();
System.out.println("thead run" + Thread.currentThread());
looper.loop();
}
}
Ответ 4
Если я отправляю обработчик в своей активности, работают ли Activity, Handler, MessageQueue и Looper все в потоке пользовательского интерфейса? Если нет, может кто-нибудь объяснить, как все это объединяется?:)
Это зависит от того, как вы создаете Handler
Случай 1:
Handler()
Конструктор по умолчанию связывает этот обработчик с Looper для текущего потока.
Если вы создаете Handler
, как это в потоке пользовательского интерфейса, Handler
связан с Looper
потока пользовательского интерфейса. MessageQueue
также связан с Looper
с потоком пользовательского интерфейса.
Случай 2:
Handler (Looper looper)
Используйте предоставленный Looper вместо стандартного.
Если я создаю HandlerThread и передаю Looper обработчика HandlerThread для обработчика, обработчика и Looper, связанные с HandlerThread, а не с потоком пользовательского интерфейса, Handler
, MessageQueue
и Looper
связаны с HandlerThread
.
Случай использования: вы хотите выполнить операцию Network or IO. Вы не можете выполнить его в потоке пользовательского интерфейса, и поэтому HandlerThread
вам подходит.
HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
Если вы хотите передать данные из HandlerThread в поток пользовательского интерфейса, вы можете создать еще один обработчик (скажем responseHandler) с помощью Looper
из потока пользовательского интерфейса и вызвать sendMessage
. Пользовательский поток responseHandler
должен переопределять handleMessage
Подробнее см. в этих сообщениях.
Какова цель Looper и как его использовать? (Для понятий)
Android: тост в потоке (например, код, связав все эти понятия)