阅读Hanlder、Looper、Message的源码

经典例子

首先是创建一个 Handler

private Handler mHandler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 0:
                mTextView.setText("更新了UI");
                break;
        }
    }
};

在子线程中更新UI

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);  // 在子线程做一段耗时操作,比如进行网络请求
            mHandler.sendEmptyMessage(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

找到看源码的切入点

从这个经典的例子中来读源码,源码的切入点就可以从 Handler 的无参构造方法开始看起。

public Handler() {
    this(null, false);
}

这个无参构造方法调用了另外一个构造方法 this(null, false);。去除方法体里面其他的无关代码,得到关键代码如下

/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with respect to synchronous messages.  Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
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;
    
    // ...
}

看一下这个两句话是什么意思

mLooper = Looper.myLooper();
if (mLooper == null) {
    throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
}

看一下 Looper.myLooper() 的实现

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

只是返回一个 Looper 对象而已,也就是获取了当前线程保存的 Looper 实例,看一下 sThreadLocal 这个变量是什么东西,用 CTRL-F 查找一下代码,整个 Looper 类中只有这几个地方有用到 sThreadLocal 变量

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
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));
}

主要看 prepare(boolean quitAllowed) 这个方法就可以了。sThreadLocal 是一个 ThreadLocal 对象,可以在一个线程中存储变量。

1 public static final void prepare() {  
2    if (sThreadLocal.get() != null) {  
3        throw new RuntimeException("Only one Looper may be created per thread");  
4    }  
5    sThreadLocal.set(new Looper(true));  
6 }

第 5 行新建一个 Looper 对象放入 sThreadLocal 对象。2~4 行判断 sThreadLocal 中是否已经有 Looper 对象了,如果有就会抛出异常,说明一个线程只能有一个 Looper 对象。

看一下 new Looper(true) 这句话做了什么事情

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

创建了一个消息队列 MessageQueue,还有获取了当前线程的实例。

到这里就不知道要看什么了,那就看一下类的注释好了,说不定有线索

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  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();
  *      }
  *  }</pre>
  */
public final class Looper {
    // ...
}

注释说 Looper 类是用来为一个线程创建一个消息循环用的。线程默认是没有消息循环的,可以调用 prepare() 创建消息循环,调用 loop() 去处理消息。通常情况下是用 Handler 类和消息循环交互。

这段注释提到了 prepare()loop() 方法。prepare() 方法刚才已经看过了,那就看一下 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) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // ...

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // ...

        try {
            msg.target.dispatchMessage(msg);
        } finally {
            // ...
        }

        // ...

        msg.recycleUnchecked();
    }
}

这段代码的大概意思是

无限循环去遍历消息队列,取出队列中的 Message 对象,然后调用 msg.target.dispatchMessage(msg); ,然后再把 Message 回收了

msg.target.dispatchMessage(msg); 这句话不明白,进入 Message 类看一下是什么东西。

Handler target;

从这里可以知道 HandlerMessage 有一腿。

再看一下 Handler 类中 dispatchMessage(msg) 方法的实现

/**
 * 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);
    }
}

这里应该就是调用各种回调方法了。从刚才写的经典例子中,我没有写任何的回调方法,因此这个方法体最后就进入了 handleMessage(msg); 方法中。看一下这个方法做了什么

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

这个方法就是经典例子中重写的 handlerMessage 方法

private Handler mHandler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 0:
                mTextView.setText("更新了UI");
                break;
        }
    }
};

现在梳理一下

  • Looper 在 prepare() 方法中创建一个 Looper 实例和一个消息队列
  • Looper 在 loop() 方法中不断地遍历消息队列,调用 MessageQueue.next() 取出 Message,然后调用和 Message 绑定的 HandlerdispatchMessage 方法,去调用各种回调方法

现在几个疑问

  • Message 是怎么创建的?
  • Message 是怎么进入 Looper 的消息队列的?
  • Handler 是怎么和 Message 绑定在一起的?

Message 是怎么创建的?

在使用 Handler 异步更新 UI 的时候,都会创建 Message 携带我们想要传递的数据。Message 有几个属性可以携带数据:

whatarg1arg2obj 。用法如下

Message msg = mHandler.obtainMessage();
msg.what = 1;
msg.arg1 = 2;
msg.arg2 = 3;
msg.obj = myObj // myObj 是我们想通过 Message 携带的对象
mHandler.sendMessage(msg);

看一下 mHandler.obtainMessage() 做了什么

/**
 * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
 * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
 *  If you don't want that facility, just call Message.obtain() instead.
 */
public final Message obtainMessage()
{
    return Message.obtain(this);
}

又调用了 Message.obtain(this),再进入看看

/**
 * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
 * @param h  Handler to assign to the returned Message object's <em>target</em> member.
 * @return A Message object from the global pool.
 */
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

可以看到,为了提高性能,先调用 Message m = obtain(); 从消息池中取出一个 Message,然后再将 Handler 实例赋值给 Messagetarget 属性,这样就将 HandlerMessage 绑定在一起了。

Message是怎么和Handler绑定在一起的?

从上面的

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

我们已经知道了

Message 是怎么进入 Looper 的消息队列的?

看一下刚才写的代码

Message msg = mHandler.obtainMessage();
msg.what = 1;
msg.arg1 = 2;
msg.arg2 = 3;
msg.obj = "想要携带的对象";
mHandler.sendMessage(msg);

Message 已经通过 Handler.obtainMessage() 获取到了,然后就是调用 Handler.sendMessage(Message msg) 发出消息。看一下 mHandler.sendMessage(msg); 做了什么

/**
 * Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received in {@link #handleMessage},
 * in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

又调用了另外一个方法,再点进去看看

/**
 * Enqueue a message into the message queue after all pending messages
 * before (current time + delayMillis). You will receive it in
 * {@link #handleMessage}, in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

怎么又调用了另外一个方法,再点进去看看

/**
 * Enqueue a message into the message queue after all pending messages
 * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
 * Time spent in deep sleep will add an additional delay to execution.
 * You will receive it in {@link #handleMessage}, in the thread attached
 * to this handler.
 * 
 * @param uptimeMillis The absolute time at which the message should be
 *         delivered, using the
 *         {@link android.os.SystemClock#uptimeMillis} time-base.
 *         
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
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);
}

从这个方法体可以看出,是把刚才我们创建的 Message 加入队列中去。这里有句话

MessageQueue queue = mQueue;

这个消息队列是哪里来的?Handler 类中怎么会有消息队列?用 CTRL-F 查找一下 mQueue = 在哪里出现过

/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with respect to synchronous messages.  Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler 的这个构造方法中找到了 mQueue 被赋值的地方

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 的静态方法 myLooper() 获取到 Looper 对象,然后再通过 Looper 对象进而获取到 Looper 的消息队列的。

知道了 Handler 中的 mQueue 是怎么获取到的,那就再接着往下看

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);
}

这个方法又调用了 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);
}

在这里,这个方法调用了消息队列 MessageQueueenqueueMessage() 方法把 Message 放进去。

enqueueMessage() 这个方法的方法体代码太多,我看不懂。不过只需要知道一件事,这个方法的指责是将 Message 放入 MessageQueue 就好了。

梳理Looper、Handler、Message、MessageQueue关系

  • Looper 在 prepare() 方法中创建一个 Looper 实例和一个消息队列
  • Looper 在 loop() 方法中不断地遍历消息队列,调用 MessageQueue.next() 从消息队列中取出 Message,然后调用和 Message 绑定的 HandlerdispatchMessage 方法,去调用各种回调方法
  • Handler 的构造方法会获得当前线程中的 Looper 实例,进而和 MessageQueue 关联在一起
  • Handler 调用 sendMessage(Message msg),会将 msg.target 赋值为自己,然后放入 MessageQueue
  • 我们在构造 Handler 实例的时候,会重写 handleMessage 方法,也就是 msg.target.dispatchMessage(msg) 最终调用的方法

Looper.prepare()又是谁来调用的?

从刚才的源码中,可以知道是通过调用 Looper.prepare() 来创建 Looper 对象。同时,从经典例子中发现,我们没有去调用 Looper.prepare() 方法。那么 Looper.prepare() 又是谁来调用的?

查了下资料,Android 应用程序的入口是 ActivityThreadmain 函数,那就去看一下 main 函数是怎么实现的,可以看到,在方法体中有一句 Looper.prepareMainLooper();

public final class ActivityThread {

    public static void main(String[] args) {
        // ...

        Looper.prepareMainLooper();

        // ...
        
        Looper.loop();      
    }
}

看一下这个方法的方法体

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

它调用了 prepare() 创建了 Looper 实例。

以上就是 UI 线程 Looper 创建过程

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容