Обработчики, MessageQueue, Looper, все ли они работают в потоке пользовательского интерфейса?
Я пытаюсь обернуть голову вокруг резьбы, и я знаю, что могу использовать Handler
для отправки сообщений / runnables в MessageQueue
, который, в свою очередь, подхватывается Looper
и отправлено обратно обработчику для обработки.
если я отправляю сообщение обработчику в своей деятельности, это Activity
, Handler
, MessageQueue
и Looper
все работает в потоке пользовательского интерфейса? Если нет, может кто-нибудь объяснить, как все это происходит? :)
4 ответов
короткий ответ: все они работают в одном потоке. Если экземпляр создан из Activity
обратный вызов жизненного цикла, все они работают в основном потоке пользовательского интерфейса.
ответ:
нить может иметь Looper
, которая содержит MessageQueue
. чтобы использовать этот объект, вам нужно будет создать Looper
в текущем потоке, вызвав (статический)Looper.prepare()
, а затем запустите цикл, вызвав (также статический)Looper.loop()
. Они статичны, потому что должен быть только один Looper
в каждом потоке.
вызов loop()
обычно не возвращает в течение некоторого времени, но продолжает принимать сообщения ("задачи"," команды " или как вы их называете) из MessageQueue
и обрабатывает их по отдельности (например, вызывая a Runnable
содержится в сообщении). Если в очереди не осталось сообщений, поток блокируется до появления новых сообщений. Чтобы остановить Looper
, вы должны позвонить quit()
на нем (который, вероятно, не останавливает цикл сразу, а скорее устанавливает частный флаг, который периодически проверяется из цикла, сигнализируя ему остановиться).
однако вы не можете добавлять сообщения в очередь напрямую. Вместо этого, вы регистрируете MessageQueue.IdleHandler
ждать queueIdle()
обратный вызов, в котором вы можете решить, хотите ли вы что-то или нет. Все обработчики вызываются по очереди. (Так "очередь" на самом деле не очередь, а вместо этого коллекция обратные вызовы должны вызываться регулярно.)
Примечание относительно предыдущего абзаца: это я на самом деле догадался. Я не мог найти никакой документации по этому поводу, но это имело бы смысл.
обновление: посмотреть ahcox комментарий и ответ.
поскольку это много работы, фреймворк предоставляет Handler
класс, чтобы упростить вещи!--43-->. При создании Handler
экземпляр, он (по умолчанию) привязан к Looper
уже подключен к текущему потоку. (The Handler
знает, что Looper
для присоединения, потому что мы вызвали prepare()
ранее, который, вероятно, сохранил ссылку на Looper
на ThreadLocal
.)
С Handler
, вы можете просто позвонить post()
"поставить сообщение в очередь сообщений потока" (так сказать). The Handler
позаботится обо всех IdleHandler
обратный звонок вещи и убедитесь, что ваш комментария Runnable
выполняется. (Он также может проверить, правильно ли время уже, если вы отправили с задержкой.)
просто чтобы быть ясным: единственный способ на самом деле сделать петлевой поток do что-то должно отправить сообщение в его цикл. Это действительно до тех пор, пока вы не вызовете quit() на looper.
относительно потока пользовательского интерфейса android: в какой-то момент (возможно, перед любыми действиями и тому подобное создано) платформа настроила Looper
(содержащей MessageQueue
) и запустил его. С этого момента все, что происходит в потоке пользовательского интерфейса через эту петлю. Это включает управление жизненным циклом деятельности и так далее. Все обратные вызовы, которые вы переопределяете (onCreate()
, onDestroy()
...) по крайней мере indirecty отправляется из этого цикла. Это можно увидеть, например, в трассировке стека исключения. (Вы можете попробовать, просто напишите int a = 1 / 0;
где-то в onCreate()
...)
я надеюсь, что это обретать смысл. Извините за то, что был неясен ранее.
Продолжение части вопроса "как все это происходит". Как писал user634618, looper берет на себя поток, основной поток пользовательского интерфейса в случае
главное приложение Looper
.
-
Looper.loop()
извлекает сообщения из очереди сообщений. Каждое сообщение имеет ссылку на связанный обработчик, которому оно должно быть возвращено (целевой элемент). - внутри
Looper.loop()
для каждого сообщения из очереди:-
loop()
вызовыpublic void Handler.dispatchMessage(Message msg)
используя обработчик, который хранится в сообщении в качестве его целевого члена. - если сообщение имеет выполняемый элемент обратного вызова, это выполняется.
- еще, если обработчик имеет общий набор обратного вызова, который выполняется.
- еще, обработчик
handleMessage()
вызывается с сообщением в качестве аргумента. (Обратите внимание, если вы обработчик подкласса, как AsyncTask, вы можете переопределитьhandleMessage()
как он это делает.)
-
на ваш вопрос о все сотрудничающие объекты, находящиеся в одном потоке UI,
а Handler
должен быть создан в том же потоке, как Looper
что он пошлет
сообщения.
Его конструктор будет искать тег Looper
и сохраните его как член, связывая
the Handler
этой Looper
.
Она также будет ссылаться на то, что Looper
очередь сообщений непосредственно в собственном члене.
The Handler
можно использовать для отправки работы в Looper
из любого потока, но это
идентификатор очереди сообщений, направляет работу на Looper
' s нитка.
когда мы запускаем некоторый код в другом потоке и хотим отправить Runnable для выполнения в потоке UI, мы можем сделать это следующим образом:
// 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();
}
Я пытаюсь реализовать этот интерфейс самостоятельно, чтобы понять концепцию. По простоте, Просто используйте интерфейс по необходимости. Вот мой тестовый код:
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();
}
}
если я отправляю сообщение обработчику в моей деятельности, выполняется ли Activity, Handler, MessageQueue и Looper в потоке пользовательского интерфейса? Если нет, может кто-нибудь объяснить, как все это происходит? :)
это зависит от того, как вы создадите проводник
Пример 1:
Handler()
конструктор по умолчанию связывает этот обработчик с Looper для текущего потока.
если вы создаете Handler
как это в потоке пользовательского интерфейса, Handler
связан с Looper
потока пользовательского интерфейса. MessageQueue
также связано с Looper
С потоком пользовательского интерфейса.
Пример 2:
Handler (Looper looper)
используйте предоставленный петлитель вместо стандартного.
если я создам HandlerThread и передать петлителя в HandlerThread в обработчик, обработчик и петлитель связаны с HandlerThread и не в потоке UI. Handler
, MessageQueue
и Looper
связано с HandlerThread
.
вариант использования: вы хотите выполнить операцию сети или ввода-вывода. Вы не можете выполнить его в потоке UI и, следовательно,HandlerThread
очень удобно для вас.
HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
если вы хотите передать данные из HandlerThread в поток пользовательского интерфейса, вы можете создать еще один обработчик (скажем, responseHandler ) с Looper
из потока пользовательского интерфейса и вызова sendMessage
. Поток пользовательского интерфейса responseHandler
должны переопределить handleMessage
см. эти сообщения для получения дополнительной информации подробности.
какова цель Looper и как его использовать? ( по понятиям )
Android: тост в потоке (например, код, связывая все эти понятия)