Android消息循环机制

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/51707527



Android的消息循环机制主要先关的类有:

  • Handler
  • Looper
  • Message
  • MessageQueue
  • ActivityThread

实际上

应用程序启动的时候,会创建一个UI线程,然后该线程关联一个消息队列,相关操作封装一个个消息放入队列中,主线程会不断循环从队列中取出消息进行分发处理。


为什么用Handler

大家都知道,Android规定【访问UI只能在主线程中进行】,如果在子线程中访问UI,程序会出现异常。

    throw new CalledFromWrongThreadException("only the original thread that created a view hierarchy can touch its views.");

所以只能在主线程中访问UI,但是Android又不建议在主线程中做耗时操作,比如IO操作、网络请求等操作,否则容易引起程序无法响应(ANR)。所以想这些耗时操作,都会放到其他的线程中进行处理,但是非UI线程又无法操作UI,所以 Handler 就派上用场了。

Handler的作用就是将一个任务切换到另外一个线程中执行。而我们主要用它来 更新UI


Handler的基本使用

先来看看Handler的基本使用。

它的使用方法分为两套: “post”方法和“send”方法。

    private Handler handler2 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    testview.setText("处理完毕");
                    break;
                default:
                    break;
            }
        }
    };
    new Thread(){
            @Override
            public void run() {
                // 一些耗时的操作

                handler.sendEmptyMessage(1);
            }
        }.start();
    handler.post(new Runnable() {
            @Override
            public void run() {
                testview.setText("post");
            }
        });

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                testview.setText("postDelayed");
            }
        }, 5000);

send 系列有 7 个方法:

    /**
     * 1. 发送一个消息
     */
    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    /**
     * 2.  发送一个空消息
     */
    public final boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what, 0);
    }
    /**
     * 3. 定时发送一个消息
     */
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    /**
     * 4.  延迟发送一个空的消息
     *     内部调用了 sendMessageDelayed() 方法
     */
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    /**
     * 5. 发送一个消息到消息队列的头部
     */
    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }
    /**
     * 6. 定时发送一个消息
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    /**
     * 7. 延迟delayMillis时间发送一个消息
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

通过以上代码可以发现,重载方法相互调用,最终都是调用了enqueueMessage()方法。


post 系列有 5 个方法:

    /**
     * 1.
     */
    public final boolean post(Runnable r){
       return sendMessageDelayed(getPostMessage(r), 0);
    }
    /**
     * 2.
     */
    public final boolean postDelayed(Runnable r, long delayMillis){
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    /**
     * 3.
     */
    public final boolean postAtTime(Runnable r, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    /**
     * 4.
     */
    public final boolean postAtFrontOfQueue(Runnable r){
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }
    /**
     * 5.
     */
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

从以上代码中可以看出,post的这几个方法都是调用的send的先关方法。只不过 通过getPostMessage()的几个重载方法,将Runnable封装成了Message

来看看getPostMessage的重载方法:

    /**
     * 设置消息对象的callback, 返回Message对象
     * 当调用dispatchMessage()方法的时候,判断Message的callback是否为空,不为空时,调用callback的run()方法。
     */
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    /**
     * 将Runnable和Object封装成一个Message对象返回。
     */
    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }

一共有两个,都是通过 Message.obtain() 方法获取一个消息对象,然后重新对内部变量赋值,然后返回该Message对象。

我们先记下这个方法,稍后进行探索。



综上,不管是post方法,还是send方法,最后都牵扯到 enqueueMessage 这样一个方法。

    /**
     * 将message添加到消息队列
     */
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; // 这里将message的target对象赋为handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

该方法内部,将message的target赋值为当前Handler对象,可见 target是Handler类型的对象 。

最后调用了queue.enqueueMessage()对象。

queue是MessageQueue类型的变量 ,表示一个消息队列。我们先不管该变量什么时候初始化的,先看看这个方法。

    Message mMessages;

    /**
     * 消息入队操作
     */
    boolean enqueueMessage(Message msg, long when) {
        // ...

        synchronized (this) {
            // ...

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;

            //当mMessage为空 或者 when是0 后者when小于对头的when值时,将当前msg作为对头。
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 当msg添加到队列中间。

                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // ...
        }
        return true;
    }

从上面代码可以看出,消息队列实际上是通过 单链表 的结构来实现的。其内部逻辑也好懂,就是通过对when和当前对头指针的一些判断逻辑,进而将参数中的message添加到单单链表中。



说到这里,进行一个小总结:

当我们使用Handler的时候,通过post或者send的一些列方法时,实际上是把一个Message(消息)添加到MessageQueue(消息队列)中去。


Looper

Looper可以称之为“消息循环”。我们将消息放到消息队列之后,还需要通过Looper从队列中取出消息进行处理。

主线程也就是ActivityThread,它被创建的时候,会调用 ActivityThread.main() 方法。

ActivityThread.java 的部分源码:

    static Handler sMainThreadHandler;  // set once in main()

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        //...

        Process.setArgV0("<pre-initialized>");

        //初始化当前线程为looper
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        //开始运行线程中的 消息队列 -- message queue
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主要代码是这两句:

  • Looper.prepareMainLooper();
  • Looper.loop();

这又牵涉到了Looper.java类。

接着往下看。

先把Looper类中的先关代码贴出来:

// sThreadLocal.get() will return null unless you‘ve called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    final Thread mThread;
    //系统调用此方法,初始化当前线程为looper,作为一个应用程序的主looper。
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//一个线程只允许存在一个looper对象。
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    //Looper构造函数 -- 私有化
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    //返回绑定到当前线程上的Looper对象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

通过以上代码,我们可以看出:

  • Looper类中维护一个消息队列和一个ThreadLocal类型的对象。
  • Looper的构造函数是private类型,私有化的。 所以只能通过 prepare(boolean) 方法来进行初始化。
  • 在Looper的构造函数中对MessageQueue进行了初始化。
  • 一个线程只允许存在一个looper对象。否则会出现运行时异常。

当ActivityThread.main方法准备好Looper之后,此时队列就和线程关联了起来。,然后调用了Looper的loop()方法。

    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {//如果me是null,则表示没有调用prepare()方法。
            throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
        }
        final MessageQueue queue = me.mQueue;// 1. 获取消息队列

        // ...

        for (;;) { // 2. 死循环--消息循环
            Message msg = queue.next(); // 3. 获取消息 (从消息队列中阻塞式的取出Message)
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // ...

            // Message对象内部有一个Handler对象target。故,实际上调用的handler的dispatchMessage
            // 故,实际上调用的handler的dispatchMessage(msg)方法进行分发。
            msg.target.dispatchMessage(msg); // 4. 分发处理消息

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // ...

            msg.recycleUnchecked();// 5. 回收消息
        }

loop方法实际上建立了一个死循环,一直从消息队列中读取消息,然后调用Message的target对象,实际上就是Handler对象的 dispatchMessage(msg) 方法进行处理。

(上面提到了,target实际上是Handler类型的对象)



接着来看Handler.dispatchMessage(msg)

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {// 当message对象中Runnable类型的callback不为空时
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg); // 调用重写的方法
        }
    }

当我们使用Handler.postxx()之类的方法时,会传入一个Runnable对象,这种情况下,“创建”的Message的callback变量不是null,dispatchMessage方法里面调用 handleCallback(msg) ,然后就调用了run()方法;

    // 调用runnable类型的run()方法
    private static void handleCallback(Message message) {
        message.callback.run();
    }

如果不是采用Handler.postxxx()之类的方法,也即callback变量为null,dispatchMessage方法里面调用handlerMessage(msg);

    // 实现该接口时,重写该方法。
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    /**
     * 使用Handler的时候,会重写该方法。
     */
    public void handleMessage(Message msg) {
    }

综上:

通过dispatchMessage(msg)方法的分发处理,就可以将事件在主线程中处理。

Handler的构造函数

上面提到,handler的7+5个方法都是调用的 MessageQueue.enqueueMessage() 方法把消息添加到消息队列中去。

实际上消息队列就是在Handler的构造函数中 获取 的。

看看它的构造方法:

    /**
     * 无参构造函数
     */
    public Handler() {
        this(null, false);
    }

    /**
     * Callback就是那个接口
     */
    public Handler(Callback callback) {
        this(callback, false);
    }

    /**
     * 通过looper对象构造
     */
    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

    /**
     * 进行一些初始化操作
     * 可以看出当使用无参的Handler的时候,mCallback对象是null。
     */
    public Handler(Callback callback, boolean async) {
        // ...

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can‘t create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;// 获取Looper中保存的消息队列
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * 指定Looper对象的构造函数很简单
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

我们最常用的就是无参的构造。

无参构造又调用了 public Handler(Callback callback, boolean async) 这个方法。

该方法内部调用了Looper类的 myLooper() 方法。

然后又从looper中取出messagequeue为Handler里的mQueue 赋值。

综上,当调用Handler的send或者post相关方法时,把消息添加进的消息队列是从Looper对象中获取的。而Looper的消息队列是new出来的,是在ActivityThread.main()中调用Looper.prepareMainLooper(),然后调用Looper.prepare(boolean)方法,在这个方法里面new了一个Looper对象,而Looper的私有构造函数中正好创建了一个消息队列。



此时,【消息产生->添加到消息队列->处理消息】 这个流程就走通了。


Message.obtain()

回过头来看getPostMessage()的两个重载方法,内部调用了 Message.obtain() 方法。

该方法用来获取一个Message对象,而不是直接new一个Message。

    // Message采用链式存储结构,内部存储指向下一个message的“指针”
    Message next;

    private static final Object sPoolSync = new Object(); //
    private static Message sPool; //
    private static int sPoolSize = 0; //消息池大小

    /**
     * 直接从message池中返回一个对象,在多数情况下,都不需要重新new一个对象,
     * 从而节省了开销。
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

看到这段代码,就发现一个“池”的概念。原来android使用了类似于“线程缓冲池”之类的“消息池”,用来保存一些处理完毕不用的Message对象,以便于下次使用时可以直接从池中获取,而不是直接创建一个新的message,从而节省了内存开销。当池中没有时,才回去new一个新的Message返回。

那既然有从池中获取的方法,当然也要有将对象放入池中的方法。

在Message类中有 recycler()recyclerUnchecked() 两个方法。

    /**
     * 将message对象回收
     */
    public void recycle() {
        if (isInUse()) { //是否还在使用
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

    /**
     * 重置状态,将消息放到消息池中
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

Message类内部维护一个链表来存储被回收的Message对象,recycler()方法会把不使用的Message对象添加此链表中。当调用handler的post系列方法时,会去构建一个message,这个message会优先从消息池中获取,如果有就复用,没有就重新创建。


总结

  • 程序启动的时候,主线程会创建一个Looper对象。Looper对象内部维护一个MessageQueue,然后调用loop()方法循环去读取消息。
  • 初始化Handler的时候,在Handler的构造函数内部,会获取当前线程的Looper对象,进而获取MessageQueue对象。由此可见,想要操作UI的Handler必须在主线程中创建。否则会提示你:【”Can’t create handler inside thread that has not called Looper.prepare()”】
  • 调用Handler的先关方法时,会获取Message对象,将消息对象的target指向当前handler对象,然后放到消息队列中。
  • loop()工作中,会从消息队列中获取一个个的消息,调用handle的dispatchMessage(msg)分发处理。
  • Message内部维护一个消息池,用来回收缓存message对象。
  • Looper相当于一个发动机,MessageQueue相当于流水线,Message相当于一个个的物品,而Handler就相当于工人。

时间: 2024-11-08 00:10:52

Android消息循环机制的相关文章

Android HandlerThread 消息循环机制之源代码解析

关于 HandlerThread 这个类.可能有些人眼睛一瞟,手指放在键盘上,然后就是一阵狂敲.立即就能敲出一段段华丽的代码: HandlerThread handlerThread = new HandlerThread("handlerThread"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()){ public void handleMessage(Message

Android Handler 消息循环机制

前言 一问起Android应用程序的入口,很多人会说是Activity中的onCreate方法,也有人说是ActivityThread中的静态main方法.因为Java虚拟机在运行的时候会自动加载指定类的静态共有main方法,因此个人更倾向于第二种说法. public final class ActivityThread { ......  public static void main(String[] args) {  ......  Looper.prepareMainLooper(); 

Android IntentService源码理解 及 HandlerThread构建消息循环机制分析

前言:前面写了Handler的源码理解,关于Handler在我们Android开发中是到处能见到的异步通信方式.那么,在Android原生里,有那些也有到了Handler机制的呢?有很多,比如我们今天所要理解分析的IntentService就使用到了Handler.接下来,我们来深入了解一下. HandlerThread: IntentService使用到了Handler+HandlerThread构建的带有消息循环的异步任务处理机制,我们先简单看一下HandlerThread是如何工作的吧.

Android消息循环分析

我们的经常使用的系统中.程序的工作一般是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的. 消息驱动的原理就是: 1. 有一个消息队列.能够往这个队列中投递消息; 2. 有一个消息循环.不断从消息队列中取出消息.然后进行处理. 在Android中通过Looper来封装消息循环.同一时候在当中封装了一个消息队列MessageQueue. 另外Android给我们提供了一个封装类.来运行消息的投递,消息的处理.即Handler. <!--more--> 在

Win32消息循环机制等【转载】http://blog.csdn.net/u013777351/article/details/49522219

Dos的过程驱动与Windows的事件驱动 在讲本程序的消息循环之前,我想先谈一下Dos与Windows驱动机制的区别: DOS程序主要使用顺序的,过程驱动的程序设计方法.顺序的,过程驱动的程序有一个明显的开始,明显的过程及一个明显的结束,因此程序能直接控制程序事件或过程的顺序.虽然在顺序的过程驱动的程序中也有很多处理异常的方法,但这样的异常处理也仍然是顺序的,过程驱动的结构. 而Windows的驱动方式是事件驱动,就是不由事件的顺序来控制,而是由事件的发生来控制,所有的事件是无序的,所为一个程

安卓中的消息循环机制Handler及Looper详解

我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handler机制来完成该功能,即当子线程中耗时操作完成后,在子线程中通过Handler向主线程发送消息,在主线程中的Handler的handleMessage方法中处理接受到的消息.这就是安卓中的消息机制,安卓中的消息机制主要是指Handler的运行机制,但是Handler的运行需要底层的MessageQu

【Dart学习】-- Dart之消息循环机制[翻译]

概述 异步任务在Dart中随处可见,例如许多库的方法调用都会返回Future对象来实现异步处理,我们也可以注册Handler来响应一些事件,如:鼠标点击事件,I/O流结束和定时器到期. 这篇文章主要介绍了Dart中与异步任务相关的消息循环机制,阅读完这篇文章后相信你可写出更赞的异步执行代码.你也能学习到如何调度Future任务并且预测他们的执行顺序. 在阅读这篇文章之前,你最好先要了解一下基本的Future用法. 基本概念 如果你写过一些关于UI的代码,你就应该熟悉消息循环和消息队列.有了他们才

android 消息提示机制

消息提示机制 一 对话框 Dialog AlertDialog 对话框有:标准对话框, 菜单式对话框, 菜单式复选框对话框,自定义对话框 (注意:这里的上下文公用了,代码的点击方法我已经在点击监听事件里注册了) 1 标准对话框: 在布局界面写好按钮,然后设置点击事件, 在事件种创建 dialog 的构造器 AlertDialog.Builder builder = new AlertDialog.Builder(context); 然后用构造器设置dialog的各种属性,最后create,sho

理解Windows消息循环机制

理解消息循环和整个消息传送机制对Windows编程十分重要.如果对消息处理的整个过程不了解,在windows编程中会遇到很多令人困惑的地方. 什么是消息(Message)每个消息是一个整型数值,如果查看头文件(查看头文件了解API是一个非常好的习惯和普遍的做法)可以发现如下一些宏定义: #define WM_INITDIALOG                   0x0110 #define WM_COMMAND                      0x0111 #define WM_L