故事:
话说有一天,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(){@Overridepublic 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) {        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{        Message prev;            for (;;) {                prev = p;                p = p.next;                if (p == null || when < p.when) {                    break;                }            }            msg.next = p; // invariant: p == prev.next            prev.next = msg;        }          if (prev != null) { // invariant: p == prev.next                msg.next = p;                prev.next = msg;            } else {                msg.next = p;                mMessages = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }          return true;}

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

讲到这里,我们已经理清了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。

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

那就是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()我并没有多讲,当然是留给聪明的你去探索了。
至此,整个消息机制就完成了。至于主线程已经自带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()方法。

更多相关文章

  1. android 学习七 一些xml layout组件的说明(自己总结不断更新)
  2. Android中的事件处理研究
  3. android线程 Handler Message Queue AsyncTask
  4. AsyncTask原理及不足
  5. 自定义View之滑动事件
  6. Android(安卓)NDK——必知必会之JNI和NDK基础全面详解(二)
  7. Android(安卓)本地文件缓存各个方法获取的路径小结
  8. 安卓(Android)+苹果(Ios)仿微信、陌陌 移动社交APP系统源码,手机
  9. android 设置Alpha值实现图片渐变效果

随机推荐

  1. Android(安卓)Paging Library 基于Recycl
  2. 实现一个用于显示当前时间的Google Andro
  3. Android REST 开发网络跳棋对战程序
  4. 浅谈Android 的线程和线程池的使用
  5. Hierarchy Viewer测试工具
  6. android 如何使用SAX解析XML
  7. 使用delphi 开发 web(五)Android 与delphi
  8. Android Studio升级后,开启时遇到tools.ja
  9. android每日一问1【2011-09-06】
  10. 【随心笔录】Android多进程实现,一个APP