我正在尝试围绕线程进行处理,并且我知道我可以使用 aHandler
将消息/可运行文件发布到MessageQueue
,而后者又被 拾取Looper
并发送回Handler
处理。
如果我Handler
在我的活动中发布到 a ,那么、Activity
和都在 UI 线程上运行吗?如果没有,有人可以解释一下这一切是如何结合在一起的吗?:)Handler
MessageQueue
Looper
我正在尝试围绕线程进行处理,并且我知道我可以使用 aHandler
将消息/可运行文件发布到MessageQueue
,而后者又被 拾取Looper
并发送回Handler
处理。
如果我Handler
在我的活动中发布到 a ,那么、Activity
和都在 UI 线程上运行吗?如果没有,有人可以解释一下这一切是如何结合在一起的吗?:)Handler
MessageQueue
Looper
简短的回答:它们都在同一个线程上运行。如果从生命周期回调实例化Activity
,它们都在主 UI 线程上运行。
长答案:
一个线程可能有一个Looper
,其中包含一个MessageQueue
。为了使用此功能,您必须Looper
通过调用 (the static) 在当前线程上创建一个Looper.prepare()
,然后通过调用 (the also static) 来启动循环Looper.loop()
。Looper
这些是静态的,因为每个线程只应该有一个。
调用loop()
通常在一段时间内不会返回MessageQueue
,但会不断从 中获取消息(“任务”、“命令”或任何您喜欢的名称)并单独处理它们(例如,通过回调Runnable
包含在消息中的 a )。当队列中没有消息时,线程会阻塞,直到有新消息。要停止 a Looper
,您必须调用quit()
它(它可能不会立即停止循环,而是设置一个从循环中定期检查的私有标志,指示它停止)。
但是,您不能直接将消息添加到队列中。相反,您注册 aMessageQueue.IdleHandler
以等待queueIdle()
回调,您可以在其中决定是否要执行某些操作。依次调用所有处理程序。(所以“队列”并不是真正的队列,而是定期调用的回调集合。)
关于上一段的注意事项:我实际上猜到了。我找不到任何关于此的文档,但这是有道理的。
因为这是很多工作,所以框架提供了Handler
类来简化事情。创建Handler
实例时,它(默认情况下)绑定到Looper
已附加到当前线程的实例。(theHandler
知道Looper
要附加什么,因为我们之前调用prepare()
过,它可能存储了对 a 的引用Looper
。ThreadLocal
)
使用 a Handler
,您只需调用post()
“将消息放入线程的消息队列”(可以这么说)。将Handler
处理所有IdleHandler
回调内容并确保您发布Runnable
的内容被执行。(如果您延迟发布,它也可能会检查时间是否正确。)
只是要明确一点:真正使循环线程做某事的唯一方法是将消息发布到它的循环中。这在您调用 looper 上的 quit() 之前是有效的。
关于 android UI 线程:在某些时候(可能在创建任何活动等之前),框架已设置 a Looper
(包含 a MessageQueue
)并启动它。从这一点开始,UI 线程上发生的一切都通过该循环。这包括活动生命周期管理等。您覆盖的所有回调 ( onCreate()
, onDestroy()
...) 至少是从该循环中间接分派的。例如,您可以在异常的堆栈跟踪中看到这一点。(你可以试试,写int a = 1 / 0;
在onCreate()
...的某个地方)
我希望这是有道理的。抱歉之前不清楚。
跟进问题的“这一切是如何结合在一起的”部分。正如 user634618 所写,looper 接管了一个线程,即应用程序 main 的主 UI 线程Looper
。
Looper.loop()
将消息从其消息队列中拉出。每个 Message 都有一个对关联的 Handler 的引用,它将被返回给(目标成员)。Looper.loop()
从队列中获取的每条消息的
内部:loop()
public void Handler.dispatchMessage(Message msg)
使用存储在 Message 中的 Handler 作为其目标成员进行调用。handleMessage()
使用消息作为参数调用处理程序。(注意,如果你像 AsyncTask 那样子类化 Handler ,你可以handleMessage()
像它一样覆盖。)关于所有协作对象都在同一个 UI 线程上的问题,Handler
必须在与Looper
它将发送消息的线程相同的线程上创建。它的构造函数将查找 currentLooper
并将其存储为成员,将 绑定Handler
到 that Looper
。它还将Looper
直接在其自己的成员中引用该消息队列。可Handler
用于Looper
从任何线程向 发送工作,但消息队列的此标识路由要在Looper
的线程上完成的工作。
当我们在另一个线程上运行一些代码并希望发送一个 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();
}
}
如果我在我的活动中发布到处理程序,活动、处理程序、消息队列和循环器是否都在 UI 线程上运行?如果没有,有人可以解释一下这一切是如何结合在一起的吗?:)
这取决于您如何创建Handler
情况1:
Handler()
默认构造函数将此处理程序与当前线程的Looper相关联。
如果Handler
在 UI 线程中这样创建,Handler
则与Looper
UI 线程相关联。MessageQueue
也Looper
与 UI Thread 相关联。
案例二:
Handler (Looper looper)
使用提供的 Looper 而不是默认的 Looper。
如果我创建一个HandlerThread并将 HandlerThread 的 Looper 传递给 Handler,则 Handler 和 Looper 与 HandlerThread 相关联,而不是 UI Thread。Handler
,MessageQueue
并与Looper
相关联HandlerThread
。
用例:您想要执行 Network OR IO 操作。您不能在 UI 线程上执行它,因此HandlerThread
对您来说很方便。
HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
如果您想将数据从 HandlerThread 传递回 UI Thread,您可以使用Looper
from UI Thread 再创建一个 Handler(比如 responseHandler )并调用sendMessage
. UI 线程responseHandler
应该覆盖handleMessage
有关更多详细信息,请参阅这些帖子。
Looper 的目的是什么以及如何使用它? (对于概念)
Android:Toast in a thread (例如通过链接所有这些概念的代码)