处理程序,MessageQueue,Looper,它们都在UI线程上运行吗?

时间:2011-03-04 12:48:11

标签: android multithreading android-handler android-looper

我正试图绕过线程,我知道我可以使用HandlerMessageQueue发送消息/可运行的消息,而Looper又会被{Activity接收1}}并发送回处理程序进行处理。

如果我在我的活动中发布了处理程序,那么{1}},HandlerMessageQueueLooper是否都在UI线程上运行?如果没有,有人可以解释这一切是如何结合在一起的吗? :)

4 个答案:

答案 0 :(得分:65)

简答:它们都在同一个线程上运行。如果从Activity生命周期回调中实例化,它们都在主UI线程上运行。

答案很长:

主题可能包含Looper,其中包含MessageQueue要使用此功能,您必须在Looper上创建Looper.prepare()当前线程通过调用(静态)Looper.loop(),然后通过调用(也是静态的)Looper来启动循环。这些是静态的,因为每个线程只应该有一个loop()

MessageQueue的调用通常不会返回一段时间,但会继续从{{“任务”,“命令”或您喜欢的任何内容)中取消消息{1}}并单独处理它们(例如,通过回调消息中包含的Runnable)。当队列中没有剩余消息时,线程会阻塞,直到有新消息。要停止Looper,你必须在其上调用quit()(这可能不会立即停止循环,而是设置一个私有标志,从循环中定期检查,表示它停止)

但是,您无法直接向队列添加消息。相反,您注册了MessageQueue.IdleHandler以等待queueIdle()回调,您可以在其中决定是否要做某事。所有处理程序依次被调用。 (所以“队列”实际上并不是一个队列,而是一个定期调用的回调集合。)

关于前一段的注释:这实际上我已经猜到了。我找不到任何关于这方面的文件,但这是有道理的。

更新:请参阅ahcox' commenthis 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)
    • 如果邮件中有一个Runnable回调成员,那么就会运行。
    • 否则,如果处理程序具有共享回调集,则运行。
    • 否则,Handler的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线程中创建这样的HandlerHandler与UI线程的Looper相关联。 MessageQueue也与UI线程的Looper相关联。

案例2:

Handler (Looper looper)
  

使用提供的Looper而不是默认的Looper。

如果我创建一个HandlerThread并将HandlerThread的Looper传递给Handler,则Handler和Looper与HandlerThread相关联,而不是与UI Thread相关联。 HandlerMessageQueueLooperHandlerThread相关联。

使用案例:您想要执行网络或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(例如,链接所有这些概念的代码)