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