Handler和Looper解析

消息机制主要包含4个类
Message 消息
MessageQueue 消息队列,增加一个消息enqueueMessage(),取出一个消息next()
Handler 发送消息sendMessage()和处理消息handleMessage()
Looper 死循环从MessageQueue中取出消息并发送handler.dispatchMessage()

实例

class MyThread extends Thread {
    public Handler handler; 
    public void run() { 
        Looper.prepare();
        handler = new Handler() {
            public void handleMessage(Message msg) { 

            } 
        };
        Looper.loop();
     }
}

Looper

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>() 

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) { //Looper.prepare()在每个线程只允许执行一次
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

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

ThreadLocal 线程本地存储区(Thread Local Storage),每个线程都有自己私有的本地存储区,不同线程彼此不能访问对方的ThreadLocal
在这里的体现就是:虽然 sThreadLocal 是static变量,但是不同的线程调用 sThreadLocal.get() 取出来的数据是不一样的
Looper.prepare() 新建一个Looper存入static的sThreadLocal,Looper内部维护一个MessageQueue。另外,还有一个prepareMainLooper(),该方法主要在ActivityThread类中使用

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;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();
    }
}

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

Looper.loop() 不断循环直到没有消息时退出循环,但其实没有消息时,queue.next()会阻塞,直到有新消息才继续执行。msg.recycleUnchecked()把Message回收到消息池,以便重复利用。(Message解析在下面)
取出消息后,调用msg.target.dispatchMessage(msg),msg.target是Handler(Handler解析在下面)

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

Looper.quit() 最终调用的是MessageQueue.quit()

MessageQueue

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

Message next() {
    ...
    for (;;) {
        ...
        //阻塞操作,当等待超时或者消息队列被唤醒,才会继续
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
           //如果msg.target==null,则一直循环msg = msg.next,直到msg.target不为null,msg.target其实是Handler
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            ...
        }
        ...
    }
}

MessageQueue.next() nativePollOnce()取出一个message,是阻塞操作,nextPollTimeoutMillis
是下一个消息的延迟(比如handler.postDelay(r,long)里的delay),当nextPollTimeoutMillis = -1时,说明队列中没有消息,会一直阻塞。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        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");
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 如果队列里没有message,next()会阻塞,这里收到新的message,则需要唤醒next()
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            ...
        }
        if (needWake) {
            nativeWake(mPtr); //唤醒next()的阻塞
        }
    }
    return true;
}

MessageQueue.enqueueMessage() 有新消息会执行nativeWake()唤醒next()里的阻塞。
MessageQueue.removeMessages() 移除队列里所有的message,放入缓存池。

Handler

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

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;
    mCallback = callback;
    mAsynchronous = async;
}

mQueue = mLooper.mQueue 说明Handler里持有的mQueue其实是Looper的。

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.sendMessage() 最终调用Looper.mQueue.enqueueMessage(),在调用前会设置msg.target = this

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

Handler.post() 也只是把runnable封装成一个msg

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

Handler.obtainMessage() 只是调用Message.obtain()

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

Handler.dispatchMessage() msg有callback就执行msg.callback.run(),没有的话,就执行handleMessage(msg)。
其实Handler.post(r) 就相当于下面这段代码,只不过系统没有开放callback变量。

Message msg = Message.obtain();
msg.callback = new Runnable(){
    @Override
    public void run() {
            
    }
};
handler.sendMessage(msg);

Handler.handleMessage() 就是我们经常重写的方法。

Message

private static Message sPool;
private static final int MAX_POOL_SIZE = 50;

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.recycle() 把message的变量都置空,赋给static的sPool,sPool也是一个Message,Message是一个链表,通过变量next连接。这里控制最多只能存50个。

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

Message.obtain() 如果sPool里面有值,把第一个取出来。

总结

  1. Handler的构造函数,会执行mLooper = Looper.myLooper()。
  2. Handler.sendMessage() 的时候,会设置msg.target = this 然后调用 mLooper.mQueue.enqueueMessage()。
    Handler.post(runnable) 其实是msg.callback=runnable,最终也是跟sendMessage() 一样的流程。
  3. Looper.prepare()的时候会新建一个Looper并存入sThreadLocal。
  4. Looper的构造函数,会执行mQueue = new MessageQueue()。
  5. Looper.loop() 不断循环mQueue.next()取出msg,然后调用msg.target.dispatchMessage(msg)。
  6. MessageQueue 里有很多native方法,MessageQueue.next()没有消息时会阻塞,MessageQueue.enqueueMessage()收到消息时会唤醒阻塞。
  7. 新建Message最好使用Message.obtain(),会优先从缓存池里获取,避免创建对象。
  8. 我们在UI线程无需调用Looper.prepare()是因为系统已经帮我们执行过Looper.prepareMainLooper()。
  9. 子线程不能直接修改UI,是因为UI的很多方法(比如draw)都不是线程安全的,所以引入Handler和Looper,用队列的形式确保操作UI是同步安全的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容