Android 异步消息处理机制 深入理解 Looper、Handler、Message、MessageQueue四者关系

相信很多人同学面试肯定都被问到过,请问Android中的Looper , Handler , Message,MessageQueue有什么关系?本文目的首先为大家从源码角度介绍4者关系,然后给出一个容易记忆的结论。

1、概述

Handler 、 Looper 、Message、MessageQueue 这四者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。说了这一堆,那么和Handler 、 Looper 、Message有啥关系?其实Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。

2、源码解析

1、Looper

对于Looper主要是prepare()和loop()两个方法。首先看prepare()方法

public static final void prepare() {  
    //sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量
    //判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例~相信有些哥们一定遇到这个错误
    if (sThreadLocal.get() != null) {  
       throw new RuntimeException("Only one Looper may be created per thread");  
    }
    //将一个Looper的实例放入了ThreadLocal  
    sThreadLocal.set(new Looper(true));  
}  

下面看Looper的构造方法:

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

在构造方法中,创建了一个MessageQueue(消息队列)。然后我们看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.");  
    } 
    //拿到该looper实例中的mQueue(消息队列) 
    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;  
           }  
           // This must be in a local variable, in case a UI event sets the logger  
           Printer logging = me.mLogging;  
           if (logging != null) {  
              logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);  
           }  
           //调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象,下面会进行分析
           msg.target.dispatchMessage(msg); 
           if (logging != null) {  
              logging.println("<<<<< Finished to " + 
              msg.target + " " + msg.callback);  
           }  
           // Make sure that during the course of dispatching the  
           // identity of the thread wasn't corrupted.  
           final long newIdent = Binder.clearCallingIdentity();  
           if (ident != newIdent) {  
              Log.wtf(TAG, "Thread identity changed from 0x"  
                        + Long.toHexString(ident) + " to 0x"  
                        + Long.toHexString(newIdent) + " while dispatching to "  
                        + msg.target.getClass().getName() + " "  
                        + msg.callback + " what=" + msg.what);  
           }   
           //释放消息占据的资源
           msg.recycle();  
      }  
}  

接下来我们看下myLooper() 方法

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

此方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。

Looper主要作用:
1. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2. loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,于是乎:Handler登场了。

3、Handler

使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)怎么发送到MessageQueue中的

public Handler() {  
    this(null, false);  
}  
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 leaksmight 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;  
}  

通过Looper.myLooper()获取了当前线程保存的Looper实例,然后又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

然后看我们最常用的sendMessage方法

public final boolean sendMessage(Message msg)  
{  
     return sendMessageDelayed(msg, 0);  
}  
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
     Message msg = Message.obtain();  
     msg.what = what;  
     return 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);  
}  

辗转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法,我们再来看看此方法:

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

enqueueMessage中首先为meg.target赋值为this,【如果大家还记得Looper的loop方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。

现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法,下面我们赶快去看一看这个方法:

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

可以看到,上面调用了handleMessage方法,下面我们去看这个方法:

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

可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。
例如:

private Handler mHandler = new Handler()  
{  
   public void handleMessage(android.os.Message msg)  
   {  
       switch (msg.what)  
       {  
           case value:  
           //do something       
           break;  
           default:  
           break;  
       }  
   };  
};  

到此,这个流程已经解释完毕,让我们首先总结一下
1. 首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2. Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3. Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4. Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5. 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

4、Handler post

今天有人问我,你说Handler的post方法创建的线程和UI线程有什么关系?
其实这个问题也是出现这篇博客的原因之一;这里需要说明,有时候为了方便,我们会直接写如下代码:

mHandler.post(new Runnable()  
{  
   @Override  
   public void run()  
   {  
       Log.e("TAG", Thread.currentThread().getName());  
       mTxt.setText("yoxi");  
   }  
});  

然后run方法中可以写更新UI的代码,其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:

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

可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message.
注:产生一个Message对象,可以new  ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。

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

最终和handler.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue.
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:

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

第2行,如果不为null,则执行callback回调,也就是我们的Runnable对象。
好了,关于Looper , Handler , Message 这三者关系上面已经叙述的非常清楚了。
最后来张图解:

希望图片可以更好的帮助大家的记忆~~

5、后话

其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

new Thread()  
{  
      private Handler handler;  
      public void run()  
      {  
          Looper.prepare();   
          handler = new Handler()  
          {  
              public void handleMessage(android.os.Message msg)  
              {  
                  Log.e("TAG",Thread.currentThread().getName());  
              };  
           };
       Looper.loop();                                                                              
}            

写到最后

以前害怕去看android源码,还有就是觉得看着无趣。那是因为没找到方法,现在根据大神们的源码分析去看,感觉还是蛮有趣的耶。本文就是根据csdn博客大神张鸿洋的源码分析写的。其实也就改了一点点啦。。。
原文地址:
【张鸿洋的博客】
http://blog.csdn.net/lmj623565791/article/details/38377229

评论

  • MakritskayaGergo回复

    viagra feminin suisse

    <a href=http://viagraoail.com/>online viagra</a>

    <a href="http://viagraoail.com/">generic viagra online</a>

    viagra cia post

  • hot lingerie回复

    Here is a good Weblog You may Locate Exciting that we Encourage You

  • CheplanovaGergo回复

    prix viagra 50 mg pfizer

    http://viagralms.com/ - buy viagra

    <a href="http://viagralms.com/">online viagra</a>

    viagra libero di notte

  • glass anal sex toy回复

    just beneath, are a lot of entirely not related websites to ours, having said that, they're surely worth going over

  • CheplanovaGergo回复

    viagra en ligne gratuit

    http://viagralms.com/ - buy viagra

    <a href="http://viagralms.com/">cheap viagra</a>

    viagra dealers bangalore

  • VladilenGergo回复

    viagra pure europe

    <a href=http://viagraomz.com/>buy viagra</a>

    <a href="http://viagraomz.com/">buy viagra</a>

    fast canadian viagra

  • Continuing Education回复

    You made a few fine points there. I did a search on the subject and found nearly all persons will have the same opinion with your blog.

  • Busy At Home回复

    A lot of thanks for all of the effort on this web site. My aunt take interest in doing investigation and it's really simple to grasp why. My spouse and i know all of the lively way you make very important items via your blog and in addition invigorate participation from others on that idea and our own girl is now studying a whole lot. Take pleasure in the remaining portion of the new year. Your performing a superb job.

  • The Handyman回复

    I will right away seize your rss feed as I can't find your e-mail subscription link or e-newsletter service. Do you have any? Kindly let me know in order that I may subscribe. Thanks.

  • Grow Our Garden回复

    Thank you for all your work on this blog. Debby really likes managing research and it is easy to understand why. A lot of people hear all about the powerful medium you give great tips and tricks by means of this blog and as well recommend contribution from others on the area then our favorite princess has been studying a great deal. Take advantage of the remaining portion of the year. You're the one carrying out a good job.

  • MakritskayaGergo回复

    viagra y popper

    <a href=http://viagraoail.com/>cheap viagra</a>

    <a href="http://viagraoail.com/">buy generic viagra</a>

    where to buy viagra soho

  • MakritskayaGergo回复

    viagra buy uk cheap

    <a href=http://viagraoail.com/>buy viagra</a>

    <a href="http://viagraoail.com/">cheap viagra online</a>

    viagra generique pharmaci

  • Carlos Eduardo Veiga回复

    Very nice post. I simply stumbled upon your blog and wished to mention that I've really enjoyed surfing around your blog posts. In any case I'll be subscribing to your rss feed and I am hoping you write again very soon!|

  • MakritskayaGergo回复

    viagra per sconto

    <a href=http://viagraoail.com/>buy viagra</a>

    <a href="http://viagraoail.com/">buy generic viagra</a>

    how do i buy viagra in leeds

  • MakritskayaGergo回复

    click here buy viagra now

    <a href=http://viagraoail.com/>buy generic viagra</a>

    <a href="http://viagraoail.com/">buy viagra</a>

    viagra cost target

  • CheplanovaGergo回复

    viagra man

    http://viagralms.com/ - viagra online

    <a href="http://viagralms.com/">viagra online</a>

    viagra plus online meds

  • para o bebe回复

    very handful of internet sites that take place to be detailed below, from our point of view are undoubtedly effectively really worth checking out

  • this website回复

    Some really nice and useful information on this internet site, likewise I conceive the pattern has got excellent features.

  • MakritskayaGergo回复

    online viagra force

    <a href=http://viagraoail.com/>viagra cheap</a>

    <a href="http://viagraoail.com/">online viagra</a>

    we like it order viagra pill

  • yeezy sneakers回复

    I would like to express thanks to the writer just for bailing me out of this instance. As a result of looking throughout the world wide web and seeing concepts which are not helpful, I assumed my life was done. Existing without the strategies to the issues you have solved through your main guide is a critical case, and the kind which might have in a wrong way damaged my career if I had not discovered your web blog. Your actual understanding and kindness in handling every part was important. I don't know what I would have done if I hadn't come upon such a step like this. It's possible to now look ahead to my future. Thanks so much for your specialized and amazing help. I won't be reluctant to suggest your web site to anyone who should have support on this subject matter.

  • CheplanovaGergo回复

    where to buy viagra in chicago

    http://viagralms.com/ - canadian viagra

    <a href="http://viagralms.com/">viagra cheap</a>

    lloyds pharmacy viagra plus

  • web hosting回复

    Wonderful story, reckoned we could combine a handful of unrelated information, nonetheless truly really worth taking a appear, whoa did one particular master about Mid East has got far more problerms too

  • VladilenGergo回复

    fast get viagra continue

    <a href=http://viagraomz.com/>buy viagra online</a>

    <a href="http://viagraomz.com/">buy viagra online</a>

    safe maximum viagra dosage

  • VladilenGergo回复

    viagra trial canada

    <a href=http://viagraomz.com/>viagra online</a>

    <a href="http://viagraomz.com/">buy viagra</a>

    viagra medical advice

  • CheplanovaGergo回复

    viagra doesnt always work

    http://viagralms.com/ - buy viagra online

    <a href="http://viagralms.com/">viagra cheap</a>

    effet viagra 50 mg