我正在尝试理解线程,我知道可以使用Handler
将消息/可运行项发布到MessageQueue
,然后由Looper
拾取并发送回Handler
进行处理。
如果我在活动中向Handler
发布消息,那么Activity
、Handler
、MessageQueue
和Looper
是否都在UI线程上运行?如果不是,能否有人解释一下这一切是如何协同工作的呢? :)
我正在尝试理解线程,我知道可以使用Handler
将消息/可运行项发布到MessageQueue
,然后由Looper
拾取并发送回Handler
进行处理。
如果我在活动中向Handler
发布消息,那么Activity
、Handler
、MessageQueue
和Looper
是否都在UI线程上运行?如果不是,能否有人解释一下这一切是如何协同工作的呢? :)
简短回答:它们都在同一个线程上运行。如果从Activity
的生命周期回调中实例化,它们都在主UI线程上运行。
详细回答:
一个线程可以拥有一个包含MessageQueue
的Looper
。为了使用这个功能,你需要通过调用(静态)Looper.prepare()
在当前线程上创建一个Looper
,然后通过调用(也是静态的)Looper.loop()
来启动循环。这些是静态的,因为每个线程只应该有一个Looper
。
通常情况下,调用loop()
不会立即返回,而是不断地从MessageQueue
中取出消息("任务"、"命令"或其他你想称呼它们的东西)并逐个处理它们(例如通过回调消息中包含的Runnable
)。当队列中没有消息时,线程会阻塞,直到有新消息。要停止一个Looper
,你必须在它上面调用quit()
(这可能不会立即停止循环,而是设置一个私有标志,定期从循环中检查,以发出停止信号)。
MessageQueue.IdleHandler
来等待queueIdle()
回调,在其中决定是否要执行某些操作。所有处理程序依次被调用。(因此,“队列”实际上不是队列,而是一组定期调用的回调集合。)Handler
类来简化事情。当您创建一个Handler
实例时,默认情况下会将其绑定到当前线程已连接的Looper
上。(Handler
知道要附加到哪个Looper
,因为我们之前调用了prepare()
,该方法可能在ThreadLocal
中存储了对Looper
的引用。)Handler
,你只需要调用post()
就可以"将消息放入线程的消息队列中"(这么说吧)。Handler
会处理所有IdleHandler
回调的事情,并确保您发布的Runnable
被执行。(如果您延迟发布,则还可能检查时间是否已经到了。)
需要明确的是:实际上让循环线程做某事的唯一方法是向其循环发送消息。在调用looper的quit()之前,这是有效的。
关于安卓UI线程:在某个时刻(很可能是在任何活动和类似的东西被创建之前),框架已经设置好了一个Looper
(包含一个MessageQueue
)并启动它。从这一点开始,UI线程上发生的所有事情都是通过该循环进行的。这包括活动生命周期管理等等。您重写的所有回调函数(onCreate()
、onDestroy()
等)至少间接地从该循环分派。例如,在异常的堆栈跟踪中可以看到这一点。(您可以尝试一下,在onCreate()
中某处写入int a = 1 / 0;
...)
我希望这有意义。之前没有表达清楚,对不起。
关于"如何组合在一起"的问题,用户634618提到,looper会接管一个线程,在应用程序主ui线程的情况下。
Looper.loop()
从消息队列中取出Message。每个Message都有一个与之关联的Handler的引用(即目标成员)。Looper.loop()
内部:
loop()
调用存储在Message中的Handler作为其目标成员使用的public void Handler.dispatchMessage(Message msg)
。handleMessage()
。(请注意,如果像AsyncTask一样子类化Handler,则可以覆盖handleMessage()
)在您关于所有协作对象都在同一个UI线程上的问题上,
必须在与它将发送消息的Looper相同的线程上创建Handler
。
它的构造函数将查找当前Looper
并将其存储为成员变量,将Handler
绑定到该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();
}
Handler()
Handler
,则Handler
将与UI线程的Looper
关联。 MessageQueue
也与UI线程的Looper
关联。Handler (Looper looper)
HandlerThread
对您很有用。 HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
为了理解这个概念,我尝试自己实现这些接口。 为简单起见,只使用必要的接口。 以下是我的测试代码:
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();
}
}
MessageQueue
中有一个真正的队列。Message
包含对MessageQueue
使用的引用,以形成一个侵入式的链接列表队列。您可以在MessageQueue.pullNextLocked()
(非公共方法)中看到从其前面抓取消息。只有当此消息队列为空时,空闲处理程序才会被调用。最后的选择是线程等待下一个延迟的Message
截止日期或出现新的Message
。 - ahcox