• 不要心急,一点一点的进步才是最靠谱的。

读完本文你将了解:

    • 前言
    • Message
      • 如何获取一个消息
      • Message.obtain()
      • 消息的回收利用
    • MessageQueue
      • MessageQueue 的属性
      • 何时初始化
      • 消息入队的过程
      • 消息出队的过程
    • Looper
      • 线程相关 ThreadLocal
      • 无限循环调度
      • 如何停止
    • Handler
      • Handler 的属性
      • 发送消息
      • 处理消息
      • 移除消息
    • 主线程消息机制
    • 总结
    • Thanks

前言

本来我以为自己很了解 Handler,在印象中 Android 消息机制无非就是:

  1. Handler 给 MessageQueue 添加消息
  2. 然后 Looper 无限循环读取消息
  3. 再调用 Handler 处理消息

整体的流程有了,但是一直没有结合源码捋一捋。

直到有一天在使用 Handler 发送消息时遇到了一个问题:

This message is already in use.

这才去翻了翻源码,今天总结一下。

Android 消息机制主要涉及 4 个类:

  • Message

  • MessageQueue

  • Handler

  • Looper

我们依次结合源码分析一下。

Message

“消息机制”,其中要传递的就是 Message,官方对它的描述是:

包含任意类型的对象和描述信息,可以被发送给 Handler。

Message 的主要属性如下:

    //用来标识一个消息,接收消息方可以根据它知道这个消息是做什么的    public int what;    //如果你的消息要传递的数据是整型的,可以直接使用 arg1 和 arg2,而不需要使用构造一个 Bundle    public int arg1;    public int arg2;    //一个任意类型的对象,在使用 Messenger 跨进程传递消息时,通常使用它传递给接收者    //在其他场景下我们一般使用 setData() 方法    public Object obj;    //负责回复消息的 Messenger,有的场景下(比如接受者、发送者模型)需要使用它    public Messenger replyTo;    //当前消息的标志,只在被 Messenger 传递消息时使用,其他情况下都是 -1    public int sendingUid = -1;    //标识当前消息是否在被使用    //当一个消息入队时这个标志会被改变,在被重新获取后重置    //当一个消息已经在被使用时,二次入队或者回收会报错(这就是我前言中提到的错误原因)    /*package*/static final int FLAG_IN_USE = 1 << 0;    //标识当前 消息是否是异步的    /*package*/static final int FLAG_ASYNCHRONOUS = 1 << 1;    //在 copyFrom 方法中要清除的标志    /*package*/static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;    /*package*/int flags;    /*package*/long when;    //很关键的数据部分    /*package*/Bundle data;    //发送和处理消息关联的 Handler    /*package*/Handler target;    //消息的回调    /*package*/Runnable callback;    //在有些场景下还会以链表的形式关联后一个消息    /*package*/Message next;    //消息池    private static final Object sPoolSync = new Object();    private static Message sPool;    //回收消息链表    private static int sPoolSize = 0;    private static final int MAX_POOL_SIZE = 50;    private static boolean gCheckRecycle = true;Pasted from: http://book2s.com/java/src/package/android/os/message.html#a940ebe121994bfb7f9629bca79beab6

可以看到,Message 中比较关键的属性有:

  • 标识干什么的 what
  • 两个简易的整型数据存储对象 arg1 arg2
  • 存储复杂点的对象 Bundle
  • 跨进程通信绑定数据的 object
  • 与之关联的 Handler
  • 还有一些标志位,和消息池什么的

如何获取一个消息

消息机制最开始肯定需要构建一个消息。

虽然 Message 的构造函数是 public 的,但是官方还是建议我们使用以下 2 种方式获取一个 Message 对象:

  1. Message.obtain()
  2. Handler.obtainMessage()

原因是这两个方法会从一个消息回收池里获取消息,而不是新建一个,这样可以节省内存。

Handler.obtainMessage() 也是调用的 Message.obtain()

    public final Message obtainMessage() {        return Message.obtain(this);    }

去看一下 Message.obtain() 源码。

Message.obtain()

Message.obtain() 有 7 个重载方法,基本就是在获取一个 Message 对象的同时直接赋值:

我们选最复杂的一个看下源码:

    public static Message obtain(Handler h, int what, int arg1, int arg2,            Object obj) {        Message m = obtain();        m.target = h;        m.what = what;        m.arg1 = arg1;        m.arg2 = arg2;        m.obj = obj;        return m;    }Pasted from: http://book2s.com/java/src/package/android/os/message.html#50de1a696587dcd5ae3c91eb63985af2

的确就是在 obtainI() 的基础上加了一些赋值。

Message.obtain() 源码如下:

    public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // 获取一个复用的消息时,会重置标志位,之前它是 FLAG_IN_USE                sPoolSize--;                return m;            }        }        return new Message();    }Pasted from: http://book2s.com/java/src/package/android/os/message.html#62dadbbac34056e7ad4fad36d757d6ef

可以看到,这个方法会获取前面提到的 private static Message sPool; ,如果 sPool 存在就从复用消息链表头部取一个消息,然后重置它的标志位;如果不存在复用消息链表就新建一个消息。

那什么时候往消息池中添加消息呢?

消息的回收利用

消息的回收在 Message.recyclerUnchecked() 方法:

    void recycleUnchecked() {        flags = FLAG_IN_USE;    //当前消息被回收时,会标志为 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++;            }        }    }

可以看到 recycleUnchecked 方法将当前 Message 标志为 FLAG_IN_USE,这样如果这个方法在被入队,就会报错。此外它还清除了其他数据,然后把这个消息加入了回收消息的链表中。

recycleUnchecked() 什么时候会被调用呢?

MessageQueueLooper 中都有。

MessageQueue.removeMessages() 方法:

    void removeMessages(Handler h, int what, Object object) {        if (h == null) {            return;        }        synchronized (this) {            Message p = mMessages;            // Remove all messages at front.            while (p != null && p.target == h && p.what == what                    && (object == null || p.obj == object)) {                Message n = p.next;                mMessages = n;                p.recycleUnchecked();    //这里调用了                p = n;            }            // Remove all messages after front.            while (p != null) {                Message n = p.next;                if (n != null) {                    if (n.target == h && n.what == what                            && (object == null || n.obj == object)) {                        Message nn = n.next;                        n.recycleUnchecked();    //这里也调用了                        p.next = nn;                        continue;                    }                }                p = n;            }        }    }Pasted from: http://book2s.com/java/src/package/android/os/messagequeue.html#0dbba2b975383e8b440199e5d69c73de

Looper.loop() 方法:

    public static void loop() {        //...        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            //...            msg.recycleUnchecked();        }    }Pasted from: http://book2s.com/java/src/package/android/os/looper.html

至此我们可以看到,一个消息在被 Looper 处理时或者移出队列时会被标识为 FLAG_IN_USE,然后会被加入回收的消息链表,这样我们调用 Message.obtain() 方法时就可以从回收的消息池中获取一个旧的消息,从而节约成本。

MessageQueue

MessageQueue 管理着一个 Message 的列表,Handlers 为它添加消息,Looper 从中取消息。

MessageQueue 的属性

    // 队列是否可以退出    private final boolean mQuitAllowed;    @SuppressWarnings("unused")    private long mPtr; //底层使用的 code    //消息链表的开头    Message mMessages;    private final ArrayList mIdleHandlers = new ArrayList();    private SparseArray mFileDescriptorRecords;    private IdleHandler[] mPendingIdleHandlers;    private boolean mQuitting;    //指出获取下一个消息的方法 next() 是否阻塞    private boolean mBlocked;    // Barriers are indicated by messages with a null target whose arg1 field carries the token.    //后一个屏障的 token     private int mNextBarrierToken;

可以看到,MessageQueue 虽然叫“消息队列”,持有的其实是一个消息链表的节点。

何时初始化

MessageQueue 一般不直接访问,都是通过 Looper.myQueue() 方法获取一个消息队列。

消息队列在 Looper 的构造函数中初始化:

private Looper(boolean quitAllowed) {    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}MessageQueue(boolean quitAllowed) {    mQuitAllowed = quitAllowed;    mPtr = nativeInit();}

MessageQueue 的构造函数中传入一个是否允许中途退出的标志,然后调用 Native 方法初始化。
这篇文章暂不研究 Native 层源码。

消息入队的过程

消息入队的方法是 enqueueMessage() 方法:

boolean enqueueMessage(Message msg, long when) {    if (msg.target == null) {    //这里要求消息必须跟 Handler 关联        throw new IllegalArgumentException("Message must have a target.");    }    if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");    }    synchronized (this) {        if (mQuitting) {    //如果消息队列已经退出,还入队就报错            IllegalStateException e = new IllegalStateException(                    msg.target + " sending message to a Handler on a dead thread");            Log.w(TAG, e.getMessage(), e);            msg.recycle();            return false;        }        msg.markInUse();    //消息入队后就标记为 在被使用        msg.when = when;        Message p = mMessages;        boolean needWake;        //添加消息到链表中        if (p == null || when == 0 || when < p.when) {                //之前是空链表的时候读取消息会阻塞,新添加消息后唤醒            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            //插入消息到队列时,只有在队列头部有个屏障并且当前消息是异步的时才需要唤醒队列            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;        }        // We can assume mPtr != 0 because mQuitting is false.        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

Message.enqueueMessage()方法中会检查入队的消息是否在被使用,如果是的话会报错:

    if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");    }

现在我们清楚了,文章开头我遇到的:

This message is already in use.

是因为我二次使用了已经在使用的消息,在入队时 MessageQueue 检查发现后报的错。

所以每次调用 Handler.sendMessage() 时,都必须是 obtain() 或者 new 一个新的 Message 对象才行。

此外,如果消息队列已经退出,还添加消息入队就会报错。

消息出队的过程

消息出队的方法是 MessageQueue.next() 方法:

Message next() {    //如果消息的 looper 退出,就退出这个方法    final long ptr = mPtr;    if (ptr == 0) {        return null;    }    int pendingIdleHandlerCount = -1; // -1 only during first iteration    int nextPollTimeoutMillis = 0;    //也是一个循环,有合适的消息就返回,没有就阻塞    for (;;) {        if (nextPollTimeoutMillis != 0) {    //如果有需要过段时间再处理的消息,先调用 Binder 的这个方法            Binder.flushPendingCommands();        }        nativePollOnce(ptr, nextPollTimeoutMillis);        synchronized (this) {            //获取下一个消息            final long now = SystemClock.uptimeMillis();            Message prevMsg = null;            Message msg = mMessages;    //当前链表的头结点            if (msg != null && msg.target == null) {                //如果消息没有 target,那它就是一个屏障,需要一直往后遍历找到第一个异步的消息                                do {                    prevMsg = msg;                    msg = msg.next;                } while (msg != null && !msg.isAsynchronous());            }            if (msg != null) {                if (now < msg.when) {    //如果这个消息还没到处理时间,就设置个时间过段时间再处理                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                } else {                    // 消息是正常的、可以立即处理的                    mBlocked = false;                    //取出当前消息,链表头结点后移一位                    if (prevMsg != null) {                        prevMsg.next = msg.next;                    } else {                        mMessages = msg.next;                    }                    msg.next = null;                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);                    msg.markInUse();    //标记这个消息在被使用                    return msg;                }            } else {                // 消息链表里没有消息了                nextPollTimeoutMillis = -1;            }            //如果收到退出的消息,并且所有等待处理的消息都处理完时,调用 Native 方法销毁队列                        if (mQuitting) {                dispose();                return null;            }            //有消息等待过段时间执行时,pendingIdleHandlerCount 增加            if (pendingIdleHandlerCount < 0                    && (mMessages == null || now < mMessages.when)) {                pendingIdleHandlerCount = mIdleHandlers.size();            }            if (pendingIdleHandlerCount <= 0) {                mBlocked = true;                continue;            }            if (mPendingIdleHandlers == null) {                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];            }            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);        }        for (int i = 0; i < pendingIdleHandlerCount; i++) {            final IdleHandler idler = mPendingIdleHandlers[i];            mPendingIdleHandlers[i] = null; // release the reference to the handler            boolean keep = false;            try {                keep = idler.queueIdle();            } catch (Throwable t) {                Log.wtf(TAG, "IdleHandler threw exception", t);            }            if (!keep) {                synchronized (this) {                    mIdleHandlers.remove(idler);                }            }        }        // Reset the idle handler count to 0 so we do not run them again.        pendingIdleHandlerCount = 0;        // While calling an idle handler, a new message could have been delivered        // so go back and look again for a pending message without waiting.        nextPollTimeoutMillis = 0;    }}

可以看到,MessageQueue.next() 方法里有一个循环,在这个循环中遍历消息链表,找到下一个可以处理的、target 不为空的消息并且执行时间不在未来的消息,就返回,否则就继续往后找。

如果有阻塞(没有消息了或者只有 Delay 的消息),会把 mBlocked这个变量标记为 true,在下一个 Message 进队时会判断这个message 的位置,如果在队首就会调用 nativeWake() 方法唤醒线程!

其中看到个 IdleHandler 是什么呢?

public static interface IdleHandler {    //当消息队列没有消息时会回调这个方法,阻塞等待有消息进入    //返回 true 的话表示唤醒阻塞的线程,false 表示移除    //如果消息队列中有消息等待在将来执行,也会调用这个方法    boolean queueIdle();}

根据源码和注释我们可以知道 IdleHandler 是一个线程阻塞时回调的接口。

MessageQueue 中提供了监听阻塞回调的注册和移除接口:

public void addIdleHandler(@NonNull IdleHandler handler) {    if (handler == null) {        throw new NullPointerException("Can't add a null IdleHandler");    }    synchronized (this) {        mIdleHandlers.add(handler);    }}public void removeIdleHandler(@NonNull IdleHandler handler) {    synchronized (this) {        mIdleHandlers.remove(handler);    }}

当消息队列阻塞时,会回调这些监听阻塞的观察者,告诉他们:我有空了!来找我玩啊!

queueIdle() 中做的操作,它会在 handler 空闲时被调用,可以充分利用 handler,我们可以在代码里这样使用:

        Handler handler = new Handler();        try {            Field field = Looper.class.getDeclaredField("mQueue");            field.setAccessible(true);            MessageQueue queue = (MessageQueue) field.get(handler.getLooper());            queue.addIdleHandler(new MessageQueue.IdleHandler() {                @Override                public boolean queueIdle() {                    //这里做一些操作                    return true;                }            });        } catch (Exception e) {        }

这里只简单介绍了 Java 层的 MessageQueue,关于 Native 层的可以看这篇文章:http://blog.csdn.net/innost/article/details/47317823

Looper

前面介绍了 Android 消息机制中消息和消息队列,有了传递的消息和存储的队列,接下来我们结合源码了解下进行调度的 Looper。

官方文档对 Looper 的介绍:

Looper 是用于运行一个线程中的消息的类。

Looper 的属性很简单:

// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal sThreadLocal = new ThreadLocal();private static Looper sMainLooper;  // 主线程中的 Looeprfinal MessageQueue mQueue;    //与之管理的消息队列final Thread mThread;    //所在的线程private Printer mLogging;private long mTraceTag;

线程中默认没有 Looper,我们需要调用 Looper.prepare() 方法为当前线程创建一个 Looper,然后就可以调用 loop() 方法调度消息。

样例代码如下:

class LooperThread extends Thread {      public Handler mHandler;      public void run() {          Looper.prepare();          mHandler = new Handler() {              public void handleMessage(Message msg) {                  // process incoming messages here              }          };          Looper.loop();      }  }

线程相关 ThreadLocal

前面讲了在一个线程中需要调用 Looper.prepare() 方法创建一个 Looper:

public static void prepare() {    prepare(true);}private static void prepare(boolean quitAllowed) {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    sThreadLocal.set(new Looper(quitAllowed));}

可以看到,一个线程中只能有一个 Looper。

创建一个或者或许当前线程的 Looper 都通过 ThreadLocal,我们来了解下它的主要源码。

ThreadLocal.get()ThreadLocal.set() 源码:

public T get() {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);        if (map != null) {        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null)            return (T)e.value;    }    return setInitialValue();}public void set(T value) {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);}ThreadLocalMap getMap(Thread t) {    return t.threadLocals;}

ThreadLocalMap 中持有一个 Entry 的数组:

private Entry[] table;static class Entry extends WeakReference<ThreadLocal> {    Object value;    Entry(ThreadLocal k, Object v) {        super(k);        value = v;    }}

可以看到,ThreadLocal 先通过当前线程获取 ThreadLocalMap ,然后在 ThreadLocalMap 中保存 ThreadLocal 和 数据的关联。

也就是一个类似 Map

无限循环调度

在线程中创建一个 Looper 以后,就可以调用 Looper.loop() 循环处理消息了,看下它的源码:

/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */public static void loop() {    final Looper me = myLooper();    if (me == null) {    //当前线程必须创建 Looper 才可以执行        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");    }    final MessageQueue queue = me.mQueue;    //底层对 IPC 标识的处理,不用关心     Binder.clearCallingIdentity();    final long ident = Binder.clearCallingIdentity();    for (;;) {    //无限循环模式        Message msg = queue.next(); //从消息队列中读取消息,可能会阻塞        if (msg == null) {    //当消息队列中没有消息时就会返回,不过这只发生在 queue 退出的时候            return;        }        //...        try {            msg.target.dispatchMessage(msg);    //调用消息关联的 Handler 处理消息        } finally {            if (traceTag != 0) {                Trace.traceEnd(traceTag);            }        }        //...        msg.recycleUnchecked();    //标记这个消息被回收    }}

可以看到,Looper.loop() 也很简单,就是调用 MessageQueue.next() 方法取消息,如果没有消息的话会阻塞,直到有新的消息进入或者消息队列退出。

拿到消息后调用消息关联的 Handler 处理消息。

可以看到,Looper 并没有执行消息,真正执行消息的还是添加消息到队列中的那个 Handler,真应了那句:解铃还须系铃人啊!

如何停止

loop() 源码中的注释就提醒我们,开启循环调度消息后不要忘记调用 quit() 方法结束循环。

Looper.quit() 和 quitSafely () 源码:

public void quit() {    mQueue.quit(false);}public void quitSafely() {    mQueue.quit(true);}

两种退出方式都调用的是 MessageQueue.quit(boolean) 方法:

void quit(boolean safe) {    if (!mQuitAllowed) {        throw new IllegalStateException("Main thread not allowed to quit.");    }    synchronized (this) {        if (mQuitting) {            return;        }        mQuitting = true;        if (safe) {            removeAllFutureMessagesLocked();        } else {            removeAllMessagesLocked();        }        // We can assume mPtr != 0 because mQuitting was previously false.        nativeWake(mPtr);    }}private void removeAllFutureMessagesLocked() {    final long now = SystemClock.uptimeMillis();    Message p = mMessages;    if (p != null) {        if (p.when > now) {    //`如果链表头部的消息执行时间在将来(也就是一时半会儿没有任务可执行)            removeAllMessagesLocked();    //就直接强硬的全部回收了        } else {                    Message n;            for (;;) {            //否则找到那个执行时间大于现在的消息,把它后面的消息都回收了                n = p.next;                if (n == null) {                    return;                }                if (n.when > now) {                    break;                }                p = n;            }            p.next = null;            do {                p = n;                n = p.next;                p.recycleUnchecked();            } while (n != null);        }    }}private void removeAllMessagesLocked() {    Message p = mMessages;    while (p != null) {    //挨个遍历链表,把消息都回收了        Message n = p.next;        p.recycleUnchecked();        p = n;    }    mMessages = null;}

可以看到 Looper 两种结束方式的区别:

  1. quit():立即把消息链表中的所有消息都回收了,比较强硬
    • 在停止后如果 Handler 还发送消息,会返回 false,表示入队失败
    • 这个方法是不安全的,一般建议使用下面那个
  2. quitSafely():比上面那个温柔一点
    • 只会将还未执行的消息回收掉
    • 在调用之后添加进入的消息不会被处理,Handler.sendMessage 也会返回 false

当消息队列被标记位退出状态时,它的 next() 方法会返回 null,于是 Looper.loop() 循环就结束了。

Handler

现在我们了解了消息队列和 Looper,接着就该介绍消息机制中最关键的角色 – Handler。

每一个 Handler 都和一个线程的 Looper 以及这个线程中的消息队列关联。

Handler 所做的就是 “线程切换”

  • 在子线程将 Message 或者 Runnable 发送到 MessageQueue 中
  • 然后等待 Looper 调度这个消息后,再召唤 Handler 来处理消息
  • 这时消息已经在创建 Handler 的线程了

这个“线程切换” 是怎么实现的呢?我们一步步揭晓。

Handler 的属性

Handler 的属性如下:

final Looper mLooper;final MessageQueue mQueue;final Callback mCallback;final boolean mAsynchronous;IMessenger mMessenger;

可以看到 Handler 的属性很简单,其中 mCallback 可以作为构造函数的参数用于新建 Handler。

public Handler(Callback callback) {    this(callback, false);}public interface Callback {    public boolean handleMessage(Message msg);}

我们平时创建 Handler 都是创建一个 Handler 的子类然后重写它的 handleMessage 方法,有了 Handler.Callback 接口我们可以用这种方式创建 Handler:

Handler mHandler = new Handler(new Handler.Callback() {    @Override    public boolean handleMessage(Message msg) {        //这里处理消息        return false;    }});

最终效果和创建 Handler 子类一样,但是需要注意,这里创建了匿名内部类,还是会持有外部引用,导致内存泄漏

发送消息

Handler 发送的主要有两种类型:

  1. Message
  2. Runnable

发送方法有 postXXX()sendXXX() 两种,postXXX 发送的是 Runnable,调用的也是 sendXXX,而 Runnable 也会被转成 Message:

    public final boolean post(Runnable r) {        return sendMessageDelayed(getPostMessage(r), 0);    }    private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

所以我们直接看 sendXXX 方法:

public final boolean sendMessage(Message msg){    return sendMessageDelayed(msg, 0);}public final boolean sendMessageDelayed(Message msg, long delayMillis){    if (delayMillis < 0) {        delayMillis = 0;    }    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}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);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    return queue.enqueueMessage(msg, uptimeMillis);}

可以看到 Handler 发送消息最后还是调用了消息队列的 enqueueMessage() 方法。

而我们常用的使用 Handler.sendMessageDelayed() 发送延迟消息,最后其实是在入队时指定这个 msg.when,在 MessageQueue.next() 方法中,会对 msg.when > now 的消息设置延迟处理,具体实现是在 Native 层。

消息入队后,Looper 如果启动了就可以从队列里循环取消息,然后调用 msg.target.dispatchMessage(msg)``` 也就是Handler.dispatchMessage()“ 方法处理消息。

处理消息

/** * Handle system messages here. */public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);    }}private static void handleCallback(Message message) {    message.callback.run();}public void handleMessage(Message msg) {}

可以看到,Handler 在处理消息时,会有三种情况:

  1. msg.callback 不为空
    • 这在使用 Handler.postXXX(Runnable) 发送消息的时候会发生
    • 这就直接调用 Runnable 的 run() 方法
  2. mCallback 不为空
    • 这在我们使用前面介绍的 Handler.Callback 为参数构造 Handler 时会发生
    • 那就调用构造函数里传入的 handleMessage() 方法
    • 如果返回 true,那就不往下走了
  3. 最后就调用 Handler.handleMessage() 方法

    • 这是一个空实现,需要我们在 Handler 子类里重写

    至此 Handler 发送和处理消息的源码我们就了解了。

移除消息

最后再看一下如果移除消息。

由于发送时可以发送 Callback 和 Message,所以取消也有两种:

  1. removeCallbacks()
  2. removeMessages()

看一下源码发现调用的其实就是消息队列的出队方法:

public final void removeCallbacks(Runnable r){    mQueue.removeMessages(this, r, null);}public final void removeMessages(int what) {    mQueue.removeMessages(this, what, null);}

主线程消息机制

我们知道,在主线程中创建 Handler 时不用 papare Looper,这是因为在主线程中系统默认创建了 Looper,它在不停地调度分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。

ActivityThread 就是我们说的主线程,而它的 main() 方法就是当主线程的入口:

public static void main(String[] args) {    /...    Process.setArgV0("");    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);    Looper.loop();}

可以看到在主线程的 main()方法中初始化了一个 Looper,然后调用了 Looper.loop() 方法开始调度消息。

发送和处理主线程中的消息的 Handler 叫做 H:

private class H extends Handler {    public static final int LAUNCH_ACTIVITY         = 100;    public static final int PAUSE_ACTIVITY          = 101;    public static final int PAUSE_ACTIVITY_FINISHING= 102;    public static final int STOP_ACTIVITY_SHOW      = 103;    public static final int STOP_ACTIVITY_HIDE      = 104;    public static final int SHOW_WINDOW             = 105;    public static final int HIDE_WINDOW             = 106;    public static final int RESUME_ACTIVITY         = 107;    public static final int SEND_RESULT             = 108;    public static final int DESTROY_ACTIVITY        = 109;    public static final int BIND_APPLICATION        = 110;    public static final int EXIT_APPLICATION        = 111;    public static final int NEW_INTENT              = 112;    public static final int RECEIVER                = 113;    public static final int CREATE_SERVICE          = 114;    public static final int SERVICE_ARGS            = 115;    public static final int STOP_SERVICE            = 116;    //...    public void handleMessage(Message msg) {        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));        switch (msg.what) {            case LAUNCH_ACTIVITY: {                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;                r.packageInfo = getPackageInfoNoCheck(                        r.activityInfo.applicationInfo, r.compatInfo);                handleLaunchActivity(r, null);                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);            } break;            case RELAUNCH_ACTIVITY: {                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");                ActivityClientRecord r = (ActivityClientRecord)msg.obj;                handleRelaunchActivity(r);                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);            } break;            //...        }        if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));    }

H 的代码很多,我们只节选一部分。从上述代码可以看到,H 内部定义了消息类型,然后根据消息的类型进行不同的处理。

主线程的消息机制如下:

  • ActivityThread 通过 ApplicationThread 与 AMS 进行进程通信
  • AMS 以进程通信的方式完成 ActviityThread 的请求后回调 ApplicationThread 的 Binder
  • 然后 ApplicationThread 向 H 发送消息,H 将消息切换到主线程,然后进行处理

总结

这篇文章结合源码完整的看了一遍 Message MessageQueue Handler Looper,现在看着上面的图,可以自信地说我“熟悉 Android 消息机制”了哈哈。

结合 Android 性能优化:多线程 理解会更深一些!

Thanks

《Android 开发艺术探索》
http://book2s.com/java/src/package/android/os/handler.html#d0bdd75e69340e6c9e876ee32501beae
http://book2s.com/java/src/package/android/os/looper.html
http://book2s.com/java/src/package/android/os/message.html

更多相关文章

  1. 【Android(安卓)Training - 05】与其他Apps进行交互 [ Lesson 2
  2. Android(安卓)Handler消息机制原理及总结
  3. Android设置应用程序默认语言
  4. android handler 的removeMessages的使用
  5. android Handler机制源码详解
  6. JNI 数据类型转换
  7. Android中有关Handler的使用详解
  8. 开机自动运行程序【Android】
  9. Android异步任务和消息机制

随机推荐

  1. Android设置里面默认存储器选项(default w
  2. android 新闻图片加载,缓存处理
  3. Android应用优化之流畅度
  4. 在Android上用Canvas绘制音频波形图
  5. 「Do.003」adb无线连接多台Android设备
  6. Android(安卓)Studio 升级3.4,Gradle升级5
  7. apk签名不一致终极解决方案
  8. 红米1S的android 4.4.4刷机到android 7.1
  9. Android使用自己的字体库让你的APP更漂亮
  10. Android多媒体学习七:访问网络上的Audio对