源码的完全解析:Android的消息机制

故事:
话说有一天,Android的源码设计师,在多线程下对UI进行更新,发现是线程不安全的,并发下出现了UI控件处于不可预期的状态,于是加上了锁机制,但发现逻辑不仅更为复杂,而且对UI控件访问的效率大大降低,所以采用了仅限主线程可以访问UI控件,不然就会报异常
接下来,子线程中获取了网络上的数据,想更新到UI上,发现不便,于是推出了消息机制

一.子线程更新UI

这时候,有人却不想在主线程更新UI,于是去分析了源码,发现:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
  1. 这个方法存在于ViewRootImpl,在requestLayout()执行。
  2. requestLayout()执行顺序,是在onResume()方法之后

所以,有人跑去onCreat() 方法里写了这串代码:

new Thread(new Runnable() {
            @Override
            public void run() {
                    textView.setText("我就要在子线程更新UI");
            }
        }).start();
  • 没错,他成功运行了起来。
二.让我们开始走进消息机制的源码世界

这是不是你们最常用的代码?

new Thread(new Runnable() {
			public void run() {
				Looper.prepare();  
				Handler handler = new Handler(){
					@Override
					public void handleMessage(Message msg) {
						Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();
					}
				};
				handler.sendEmptyMessage(1);
			};
		}).start();
为何我们总是强调Looper.prepare()必须在new Handler()前面?

不信你去试试将new Hander()放在Looper.prepare(),系统说不行:
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()

意思就是:不能在线程内部创建handler,因为他没有调用Looper.prepare()

让我们先留着这个疑问,到后面就告诉你答案。
一切的原理就从发送信息开始讲起

我们知道handler主要有两种方式传递信息:

(1)handler.sendMessage(msg)

public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
  }

(2)handler.post(Runnable run)

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 run)在getPostMessage®方法中将Runnable r 封装到Message的callback变量当中,返回Message对象。
然而(1)(2)这两种方式,都将执行Handler的**sendMessageDelayed(msg,delayMillis)**方法。
且让我们继续走进源码分析:

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

是不是很简单的代码?没错。
在sendMessageDelayed(Message msg long delayMills)只是简单的将delayMills+SystemClock.uptimeMillis()换算成msg的执行时间
然后继续执行sednMessageAtTime(消息,执行时间):在这个方法,我们发现了mQueue这个MessageQueue对象,这个对象是哪里来的呢?
然后我们来继续看源码:

public Handler() {
    this(null, false);
}
public Handler(Callback callback) {
    this(callback, false);
}
public Handler(Callback callback, boolean async) {
 ……
    mLooper = Looper.myLooper();
    mQueue = mLooper.mQueue;
……
}

没错,从Hanlder的构造函数中,我们可以看到MessageQueue对象来自Looper。你是否对Looper.prepare()在Hanlder有猜想了呢?

让我们去看看Looper的源码:

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

这里的sThreadLocal是TreadLocal对象,让我们走进源码:

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();
}
 private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

在这里我们不细讲ThredLocal和Thread以及THreadLocalMap之间的关系,你需要知道的是getMap(t)取出了当前线程中的ThreadLocalMap对象,ThreadLocalMap里面维护着一个数组Entry,类似键值对的方式保存一个对象。

这里保存的对象是什么呢?

这时候**Lopper.prepare()**方法正式上线.

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

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

我们发现了这样的异常:Only one Looper may be created per thread

意思就是:每个线程仅仅可以创建一个Looper对象。

在Looper.prepare()过程中,会判断Looper对象是否为空。

不为空,则抛出异常:Only one Looper may be created per thread

为空,则执行sThreadLocal.set(new Looper(quitAllowed))

明显,这里新建了一个Looper对象:在这个构造函数中,绑定了新建的MessageQueue对象和当前线程。

让我们再去看看 sThreadLocal.set(new Looper(quitAllowed)) 源码:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

就是这个set方法,将传进来的参数Looper对象保存到了当前线程中的TreadLocalMapEntry数组 当前。

没错,就是它,我们之前所提到sThreadLocal.get()的那个对象,就是这个传进来的参数Looper对象
讲到这里,是不是我们可以理解为何Looper.prepare()必须在handler创建之前呢?

正如我们之前所说,Looper.prepare()Looper该Looper构造函数所创建的MessageQueue mQueue关联起来,然后将该Looper保存到了当前线程中的TreadLocalMap当中。
而在new Handler的时候:即

public Handler(Callback callback, boolean async) {
 ……
mLooper = Looper.myLooper();
    mQueue = mLooper.mQueue;
……
}

Looper.myLooper()就是取当前线程的ThreadLocalMap保存的Looper对象。
用到了该Looper.mQueue对象。不然,先new Handler()将得到一个空的MessageQueue,会报异常。

所以Looper.prepare()必须在handler创建之前。

那我们可以再回去继续看Hanlder的源码。刚才是从这里游出去的:

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方法。这里将msg.target指向了该handler对象,这handler非常重要,是后面Looper.loop()方法中msg.target.dispatchMessage()将会用到,是为了找到handler,将Message msg派遣出去使用的。

然后我们就走向了queue.enqueueMessage(msg,uptimeMills)
也就是我们的MessageQueue大佬的源码世界内来:(我们依旧只放出最关键的代码行)

  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");
                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) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
    }

我们可以看到,链表将以执行时间从小到大的顺序排列,形成一条单链表。没错,MessageQueue的全局变量 Message mMessages最后就将指向msg单链表的头部。

  needWake = mBlocked && p.target == null && msg.isAsynchronous();

不知道人家有没有注意到这行代码,是否知道这是什么意思?

首先我们得知道两个概念:屏障,异步消息。

屏障:常用于UI绘制前,插入一个屏障到消息队列的头部,所有同步消息都会被阻塞。屏障的target=null;

异步消息:我们sendMessage,post方法默认都是同步消息,如果我们设置message.setAsynchronous(true),那么message就是一个异步消息,不受屏障的限制。

没有屏障的时候,理会所有消息,把队列当做一个普通队列。

有屏障的时候,理会异步消息,把队列当做一个异步消息队列。

needWake=true,只有两种情况:

  • message在消息队列的头部
  • 消息队列头部是屏障的时候,只观察异步消息,message是异步消息的第一个

说白了,needWake=true,说明阻塞所需要等待的时间需要更新了。

讲到这里,我们已经理清了Handler Looper Message Message之间的·关系了吧

我们似乎只差最后一步了?我们将Message保存起来,就是为了发送出去的吧。没错,所以Loopoer.loop()出现了。

我们依旧来看看Looper的源码(只放出其中最关键的代码行):

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

就是这里取出MessageQueue对象,将单链表中的msg按照执行时间从小到大的顺序依次使用msg.target.dispatchMessage(msg)方法派遣出去。
这里的msg.target就是我们在hander enqueueMessagae()中的msg.target=this;就是发送这个msg的handler。

我们继续去看Handler的源码:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

可以看到 handleCallback(msg)handleMessage(msg)
handleCacllback(msg)对应的就是我们handle.post(Runnable callback)方法的传递。msg对象封装着msg.callback。前面讲到post方法的时候我们已经提到过。
来看看**handleCallbck()**源码:

private static void handleCallback(Message message) {
        message.callback.run();
    }

对不对,就是我们post上去的run方法的执行。
handMessage(msg)对应的就是我们sendMessagae(msg)的传递。
来看看**handleMessage()**源码:

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

对不对,就是我们new handler()的handleMessage(msg),要求子类必须实现这个接口去接收Message。

疑惑点:mCallback,这个对象是哪里来的呢?

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

当然还有一点,但由于不想前面显得太过繁琐,所以并没有在前面提及。

那就是Looper.loop()的阻塞问题。还记得上面的loop()这段代码吗?

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

在mQueue不为空的时候,我们这里不讲,也没必要。
我们需要了解的是,当Loop.loop()方法将mQueue维护的msg对象都派遣出去,只剩下一个null的msg对象的时候,为何没有执行loop()中的if(msg==null)?
msg对象为空不是跳出loop()方法了吗?然后application就关闭了?UI控件不响应了?
UI的响应就是一种消息机制的体现

我们先看看它的上一行
源码告诉我们queue.next()这方法可能会阻塞。这是为什么,让我们走进源码区看看:
(删减不少了,看不下去我也没办法)

Message next() {
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        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;
           
            if (msg != null) {
                if (now < msg.when) {
                } else {
                    // Got a message.
                    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 {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
          
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
        // 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;
    }
}

我们看看 nativePollOnce(ptr, nextPollTimeoutMillis)这个方法:
ptr是Natvie底层Looper地址。
nextPollTimeoutMillis是阻塞标识。
nextPollTimeoutMillis=-1,一直阻塞不会超时
nextPollTimeoutMillis=0,不会阻塞,立即返回
nextPollTimeoutMillis>0,最多阻塞nextPollTimeoutMillis毫秒。
当执行:

Message msg = queue.next(); // might block

链表头部msg为空的时候
首次循环,由于nextPollTimeoutMillis = 0;
执行nativePollOnce(ptr, nextPollTimeoutMillis)并不会阻塞,立即返回。
执行 nextPollTimeoutMillis = -1;
由于
pendingIdleHandlerCount =0
执行

if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

在这里我们得到了mBlocked = true,将在新的消息来到时在enqueueMessage()用到。
接下来的代码并不执行,继续循环。
继续执行nativePollOnce(ptr, nextPollTimeoutMillis)
由于nextPollTimeoutMillis = -1,所以线程将一直阻塞。系统休眠,释放占用的资源。
直到有新的消息发送,执行MessageQueue对象的**enqueueMessage()**方法。

boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } 
 // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }

这段代码我其实在前面的**enqueueMessage()**源码中放了出来,但没有提及。

因为不提阻塞,其实提这个的意义并不大。
p=null (p就是之前的链表)
于是执行needWake=mBlocked=true
于是执行nativeWake(mPtr);
将唤醒之前一直被阻塞的nativePollOnce(ptr, nextPollTimeoutMillis)
继续执行**nativePollOnce(ptr, nextPollTimeoutMillis)**之后的代码,即:

Message prevMsg = null;
            Message msg = mMessages;
if (msg != null) {
                if (now < msg.when) {
                } else {
                    // Got a message.
                    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 {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

因为msg!=null,now >=msg.when(这里我们当msg是即时信息)
于是return msg;
于是执行Message msg = queue.next(); // might block
msg还为空吗? 它就是新发来的消息。
当然会有其他的情况产生,并不一定会完全执行我们如上所说。

你或许会想到一个问题,就是如何才能结束Looper.loop()方法?

你可以注意到了在Messsage next()方法中有这样一段源码:

    // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

当mQuitting=true的时候,next()返回null,于是就执行了Loop loop()源码:

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

然后就可以结束这个loop()方法

我们再去看看Looper源码,存在这么一个方法:

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

表面意思就是离开

既然如此,我们就去看看MessageQueue的quit(false)源码

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

你可能看到了mQuitAllowed,是不是觉得有点眼熟,让我们看看之前的源码:

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

默认是quitAllowed=true
quit(true)方法,确保了mQuitting = true;
于是当执行MessageQueue next()方法的时候,就执行如下源码:

    // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

跳出loop()方法,结束死循环。

所以我们可以使用Looper对象的quit()方法去结束Looper对象的loop()方法。

至于底层的nativeXXX(),idleHanlder,异步消息(紧急)我并没有多讲,当然是留给聪明的你去探索了。
至此,整个消息机制就完成了。至于主线程已经自带Looper了,我就不讲了。

总结:

每个线程仅仅可以创建一个Looper对象。
每个Looper对象可以对应多个hanlder对象。
每个Looper对象对应一个MessageQueue对象
每个MessageQueue维护一个Message链表
Message对象与发送消息的handler对象绑定。
Looper.prepare()执行在new Handler()前面
Looper.prepare()将新建的Looper对象保存在了当前线程的TreadLocalMap的entry数组当中。
Looper.loop()负责派遣MessageQueue还未派遣的msg对象。
handler.post(Runnable r):Runnable对象被封装到Message对象中,充当msg.callback。
使用Looper对象的quit()方法去结束Looper对象的loop()方法。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值