[Android] Handler、MessageQueue、Message以及Looper之间的关系

首先我们来看一下Google官方实例代码。如下:

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

从中我们可以看出有以下几种对象:Thread 、Handler、Message以及Looper。让我们一个一个来分析。
首先,有一个线程LooperThread,它是Handler的执行场所。如图:

点击图片放大显示-1.png

然后执行Looper.prepare()方法,我们走进去看一下源码:

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

而sThreadLocal是ThreadLocal<Looper>的实例。

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

原来调用Looper.prepare()方法后系统内部帮我们new了一个Looper,并且把这个Looper实例放入sThreadLocal中,从而保证这个Looper是当前线程唯一的。so 我们就有了一个唯一的Looper实例。如图:

点击图片放大显示-2.png

既然有了实例,那我们接着看下Looper的构造方法,看看里面做了什么。

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

原来在构造方法里面又new了一个MessageQueue实例,从名字上来看,可能是一个存放Message的队列,既然是队列,所以它是先进先出(FIFO—first in first out)的。接着画图:

点击图片放大显示-3.png

可以看出调用Looper.prepare()方法后,系统帮我们创建了Looper和MessqgeQueue实例,可以分别通过Looper.myLooper()和mLooper.mQueue获得对应的实例。

/** 
* 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();
}
...
final MessageQueue mQueue;
...

Looper.prepare()的分析暂时到这里。我们接着看下面,下面我们new了一个Handler实例,先看Handler构造方法(省略其他,最后调用的是下面这个构造方法)。

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

可以看到在Handler的构造方法里面初始化了Looper和MessageQueue对象,并且回调了handleMessage方法。如图:

点击图片放大显示-4.png

我们接着往下看,执行Looper.loop()方法。看一下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)
    {        
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");   
    }    
    final MessageQueue queue = me.mQueue;    
    // Make sure the identity of this thread is that of the local process,    
    // and keep track of what that identity token actually is.    Binder.clearCallingIdentity();    
    final long ident = Binder.clearCallingIdentity();   
    for (;;) 
    {        
        Message msg = queue.next(); // might block        if (msg == null) 
    {            
        // No message indicates that the message queue is quitting.   
        return;        
    }        
    ...   
}       
    msg.target.dispatchMessage(msg);        
...
}

首先拿到Looper的MessageQueue对象,无限循环(利用for(;;))取出MessageQueue里面的Message对象(果然MessageQueue里面放的的Message,哈哈)。取出以后调用msg.target.dispatchMessage(msg)方法。那么这个msg.target是个什么东东?看下Messqge的源码:

...
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
...

原来是Handler,其实是调用了Handler.dispatchMessage(msg)方法。还记得我们初始化Handler的时候回调了handleMessage(msg)方法吗?我们大胆的猜想下,会不会在dispatchMessage(msg)里面调用了handleMessage(msg)?看下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)方法(这B装的太假了。。。)。
原来Looper.loop()方法就是在不停的取出MessageQueue里面的Message对象,并且执行handleMessage(msg)方法,所以初始化Handler的那个handleMessage回调会在这时候被调用。看图(表吐槽。。。):

点击图片放大显示-5.png

那么问题来了:
1,Message是怎么创建的?
2,MessageQueue里面的Message对象是怎么放进去的呢(从图中可以猜出是Handler放进去的)?
3,Handler是怎么赋值给msg.target的?。
我们回想下我们使用Handler的使用。

...
mHandler.sendEmptyMessage(0x01);
...

看下Handler的sendEmptyMessage源码:

public final boolean sendEmptyMessage(int what)
{    
    return sendEmptyMessageDelayed(what, 0);
}

接着走Handler的sendEmptyMessageDelayed方法。

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 
{    
    Message msg = Message.obtain();    
    msg.what = what;    
    return sendMessageDelayed(msg, delayMillis);
}

原来Message对象在这时候被创建了,解决了问题1。再往下看Handler的sendMessageDelayed方法。

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

终于出现MessageQueue对象了,是不是要放入Message到 MessageQueue 里面呢?接着看。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
 {   
    msg.target = this;    
    if (mAsynchronous) 
    {        
        msg.setAsynchronous(true);    
    }    
    return queue.enqueueMessage(msg, uptimeMillis);
}

haha, msg.target = this,这个this是当前的Handler对象,原来在这里把Handler对象赋值给msg.target,解决问题3,接着看MessageQueen的enqueueMessage方法:

boolean enqueueMessage(Message msg, long when)
{        
    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;       
 }
...        
}

其中有一句mMessages = msg。mMessages 保存的是当前消息队列,如果它为空就把消息添加到消息队列头,否则就要找到合适的位置将当前消息添加到消息队列。还记得Message是怎么取出来的吗(在Looper.loop方法里Message msg = queue.next(); )?看下queue.next()方法:

...
Message next() {
synchronized (this) 
{    
    Message prevMsg = null;    
    Message msg = mMessages;    
    if (msg != null && msg.target == null) 
    {        
        // Stalled by a barrier.  Find the next asynchronous message in the queue.       
        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;   
               if (DEBUG) Log.v(TAG, "Returning message: " + msg);            msg.markInUse();       
     return msg;      
  }   
...
}
...

其中省去了许多代码,想看的同学可以自己去看看下。最后会返回找到的msg(其中没有分析msg.next还有msg.isAsynchronous),而Message msg = mMessages,所以在enqueueMessage里面被赋值的mMessages就被返回给了Looper.loop()里面的msg,紧接着回调msg.target.dispatchMessage()方法。这样所有的都关联起来了,大功告成。最后来张图:

点击图片放大显示-6.png

总结
1,1个线程有且只有一个Looper,在Looper里有且只有一个MessageQueue,这些对象的初始化都是在Looper.prepare()方法完成。
2,Handler往MessageQueue里面利用sendMessage方法添加Message,Looper利用Looper.loop()方法取出Message,并且回调msg.target.dispatchMessage方法.
3,在线程里必须调用一次Looper.prepare()和Looper.loop()方法。
4,主线程中怎么没见调用3中的2个方法?因为系统帮我们调用了。
(ps:由于第一次写简书,而且笔者水平有限,文中可能出现错误的地方,希望多多保函和指正。)

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

推荐阅读更多精彩内容