源码解读 Android 消息机制( Message MessageQueue Handler Looper)2

Looper

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

官方文档对 Looper 的介绍:

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

Looper 的属性很简单:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // 主线程中的 Looepr

final 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("<pre-initialized>");

    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

results matching ""

    No results matching ""