android基础知识02——线程安全3:Message,MessageQueue,Handler,Looper

    android的UI操作不是线程安全的,同时也只有主线程才能够操作UI,同时主线程对于UI操作有一定的时间限制(最长5秒)。为了能够做一些比较耗时的操作(比如下载、打开大文件等),android提供了一些列机制。《android基础知识02——线程安全》系列文章就是参考了网上许多网友的文章后,整理出来的一个系列,介绍了主要的方法。分别如下:

         android基础知识02——线程安全1:定义及例子

        android基础知识02——线程安全2:handler、message、runnable

        android基础知识02——线程安全3:Message,MessageQueue,Handler,Looper

        android基础知识02——线程安全4:HandlerThread

        android基础知识02——线程安全5: AsyncTask


        在上文例子中,用到了消息队列,这里就不得不提与之相关的Message,MessageQueue,Handler,Looper。

        线程,MessageQueue,Handler,Looper之 间的关系可以通过一个图来展示:



1、Handler

 --用来处理消息和线程

--每个Handler可以执行一个线程对象(Runnable实现)也可以发送和处理消息
>>post(Runnable)
>>postAtTime(Runnable,long)
>>postDelayed(Runnable,long)
>>sendEmptyMessage(int)
>>sendMessageAtTime(Message,long)
>>sendMessageDelayed(Message,long)
>>handleMessage(Message)
        通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。每个Handler都会与唯一的一个线程以及该线程的消息队列管理。当你创建一个新的Handler时候,默认情况下,它将关联到创建它的这个线程和该线程的消息队列。也就是说,如果你通过Handler发布消息的话,消息将只会发送到与它关联的这个消息队列,当然也只能处理该消息队列中的消息。
主要的方法有:
1)   public final boolean sendMessage(Message msg)
把消息放入该Handler所 关联的消息队列,放置在所有当前时间前未被处理的消息后。
2)   public void handleMessage(Message msg)
关联该消息队列的线 程将通过调用Handler的handleMessage方 法来接收和处理消息,通常需要子类化Handler来 实现handleMessage。
removeMessages(0)来清除消息队列 。

        这个是我从网络上找到的介绍,如果你看到handler的源代码,则其介绍这样的:

* A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 * 
 *

There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.

[java]  view plain copy
  1. public class Handler {  
  2.     private static final boolean FIND_POTENTIAL_LEAKS = false;  
  3.     private static final String TAG = "Handler";  
  4.   
  5.     public interface Callback {  
  6.         public boolean handleMessage(Message msg);  
  7.     }  
  8.     final MessageQueue mQueue;  
  9.     final Looper mLooper;  
  10.     final Callback mCallback;  
  11.     IMessenger mMessenger;  
  12.     /** 
  13.      * Subclasses must implement this to receive messages. 
  14.      */  
  15.     public void handleMessage(Message msg) {  
  16.     }  
  17.     public Handler() {  
  18.         if (FIND_POTENTIAL_LEAKS) {  
  19.             final Classextends Handler> klass = getClass();  
  20.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  21.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  22.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  23.                     klass.getCanonicalName());  
  24.             }  
  25.     }  
  26.   
  27.         mLooper = Looper.myLooper();  
  28.         if (mLooper == null) {  
  29.             throw new RuntimeException(  
  30.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  31.         }  
  32.         mQueue = mLooper.mQueue;  
  33.         mCallback = null;  
  34.     }  
  35.   
  36.     /** 
  37.      * Handle system messages here. 
  38.      */  
  39.     public void dispatchMessage(Message msg) {  
  40.         if (msg.callback != null) {  
  41.             handleCallback(msg);  
  42.         } else {  
  43.             if (mCallback != null) {  
  44.                 if (mCallback.handleMessage(msg)) {  
  45.                     return;  
  46.                 }  
  47.             }  
  48.             handleMessage(msg);  
  49.         }  
  50.     }  
  51.     public Handler() {  
  52.         if (FIND_POTENTIAL_LEAKS) {  
  53.             final Classextends Handler> klass = getClass();  
  54.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  55.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  56.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  57.                     klass.getCanonicalName());  
  58.             }  
  59.         }  
  60.   
  61.         mLooper = Looper.myLooper();  
  62.         if (mLooper == null) {  
  63.             throw new RuntimeException(  
  64.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  65.         }  
  66.         mQueue = mLooper.mQueue;  
  67.         mCallback = null;  
  68. }  
  69. public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  70.     {  
  71.         boolean sent = false;  
  72.         MessageQueue queue = mQueue;  
  73.         if (queue != null) {  
  74.             msg.target = this;  
  75.             sent = queue.enqueueMessage(msg, uptimeMillis);  
  76.         }  
  77.         else {  
  78.             RuntimeException e = new RuntimeException(  
  79.                 this + " sendMessageAtTime() called with no mQueue");  
  80.             Log.w("Looper", e.getMessage(), e);  
  81.         }  
  82.         return sent;  
  83.     }  
        从源码分析可以看出:
        1)handler在无参数的构造方法中调用Looper.myLooper()方法,里面就是从当前线程里面获取一个Looper对象,如果没有则创建.这样对Looper就进行初始化,初始化Looper的同时一并初始化MessageQueue,并且从中得到looper的MessageQueue .可以看出Handler就是Looper和MessageQueue的管理者和调度者.

        2)其中最重要的是:sendMessageAtTime(Message msg, long uptimeMillis)这个方法,当你往Handler中发送Message消息的时候,从代码看出他自己并不去处理Message ,而是交给了MessageQueue.由以下从这段代码来处理:

       3)runnable对象在处理时,是创建一个新的Message并将其mCallback设置为该runnable对象,并将该Message插入到MessageQueue。

        4)handler的功能包括两部分:处理消息(执行Message的内容)、转发消息,其中主要由handleMessage和dispatchMessage这两个函数处理;

        5)handler中不管是send message还是post runnable都是将其加入到MessageQueue 。

        queue.enqueueMessage(msg, uptimeMillis), 其具体实现要看下面的对。

2、Message

frameworks\base\core\java\android\Os\Message.java

--定义一个信息对象,该信息对象可以发送给Handler对象来处理。
--可以通过Message.obtain()或者Handler.obtainMessage()方法来实例化
        Message是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。Message中包含了两个额外的 int字段和一个object字段,这样在大部分情况下,使用者就不需要再做内存分配工作了。虽然Message的构造函数是public的,但是最好是使用Message.obtain( )或Handler.obtainMessage( )函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提供效率。

 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations in many cases.  

[java]  view plain copy
  1. public final class Message implements Parcelable {  
  2.     public int what;  
  3.     public int arg1;  
  4.     public int arg2;  
  5.     public Object obj;  
  6.     public Messenger replyTo;  
  7.     long when;  
  8.     Bundle data;  
  9.     Handler target;       
  10.     Runnable callback;   
  11.     Message next;  
  12.     private static Object mPoolSync = new Object();  
  13.     private static Message mPool;  
  14.     private static int mPoolSize = 0;  
  15.     private static final int MAX_POOL_SIZE = 10;  
When: 向Handler发送Message生成的时间
Data: 在Bundler 对象上绑定要线程中传递的数据
Next: 当前Message 对一下个Message 的引用
Handler: 处理当前Message 的Handler对象.
mPool: 通过字面理解可能叫他Message池,但是通过分析应该叫有下一个Message引用的Message链更加适合.
其中Message.obtain(),通过源码分析就是获取断掉Message链关系的第一个Message.

       对于源码的解读,可以明确两点:

        1)Message.obtain()是通过从全局Message pool中读取一个Message,回收的时候也是将该Message 放入到pool中。

        2)Message中实现了Parcelable接口。

3、MessageQueue

--维护一个消息列表,该列表中的消息通过Looper对象来分发。
--调用Looper.myQueue()方法可以获得一个MessageQueue对象
        Message Queue是一个消息队列,用来存放通过Handler发布的消息。消息队列通常附属于某一个创建它的线程,可以通过Looper.myQueue()得 到当前线程的消息队列。Android在 第一启动程序时会默认会为UI thread创建一个关联的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。 

/**

 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 *

You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */

[java]  view plain copy
  1. public class MessageQueue {  
  2.     Message mMessages;  
  3.     private final ArrayList mIdleHandlers = new ArrayList();  
  4.     private boolean mQuiting = false;  
  5.     boolean mQuitAllowed = true;  
  6.       
  7.     public static interface IdleHandler {  
  8.         boolean queueIdle();  
  9. }  
  10.   
  11. public final void addIdleHandler(IdleHandler handler) {  
  12.      if (handler == null) {  
  13.          throw new NullPointerException("Can't add a null IdleHandler");  
  14.      }  
  15.         synchronized (this) {  
  16.             mIdleHandlers.add(handler);  
  17.         }  
  18. }  
  19.   
  20. final boolean enqueueMessage(Message msg, long when) {  
  21.         if (msg.when != 0) {  
  22.             throw new AndroidRuntimeException(msg  
  23.                     + " This message is already in use.");  
  24.         }  
  25.         if (msg.target == null && !mQuitAllowed) {  
  26.             throw new RuntimeException("Main thread not allowed to quit");  
  27.         }  
  28.         synchronized (this) {  
  29.             if (mQuiting) {  
  30.                 RuntimeException e = new RuntimeException(  
  31.                     msg.target + " sending message to a Handler on a dead thread");  
  32.                 Log.w("MessageQueue", e.getMessage(), e);  
  33.                 return false;  
  34.             } else if (msg.target == null) {  
  35.                 mQuiting = true;  
  36.             }  
  37.   
  38.             msg.when = when;  
  39.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  40.             Message p = mMessages;  
  41.             if (p == null || when == 0 || when < p.when) {  
  42.                 msg.next = p;  
  43.                 mMessages = msg;  
  44.                 this.notify();  
  45.             } else {  
  46.                 Message prev = null;  
  47.                 while (p != null && p.when <= when) {  
  48.                     prev = p;  
  49.                     p = p.next;  
  50.                 }  
  51.                 msg.next = prev.next;  
  52.                 prev.next = msg;  
  53.                 this.notify();  
  54.             }  
  55.         }  
  56.         return true;  
  57.     }  
mMessages: 为当前序列的第一个Message, 通过源码分析 MessageQueue并不是对许多Message 之间的关系维护,这样也许可以省去很多事把,而Message 之间的关系
则统统丢给了Message自己去维护,这个可以从对Message源码分析可以理解.
mIdleHandler: 保存的是一系列的handler的集合.
其中final boolean enqueueMessage(Message msg, long when),
这个方法就是上面提到Handler 处理消息时调用到的方法,对她理解了就显的很重要了,其按延迟时间长短,将延迟时间短的放在前面,功能代码如下:
[java]  view plain copy
  1. msg.when = when;  
  2. Message p = mMessages;  
  3. if (p == null || when == 0 || when < p.when) {  
  4.        msg.next = p;  
  5.        mMessages = msg;  
  6.        this.notify();  
  7. else {  
  8.       Message prev = null;  
  9.       while (p != null && p.when <= when) {  
  10.                prev = p;  
  11.                p = p.next;  
  12.            }  
  13.        msg.next = prev.next;  
  14.        prev.next = msg;  
  15.        this.notify();  
  16. }  
        当向MessageQueue中添加消息的时候,判断当前的Message(mMessage)是否为空,
        如果为空或者when=0或者when

       如果不为空: 则循环把当前的Message(mMessage)的下一个Message(next)进行遍历,用prev记住当前的message,直到找到prev的下一个Message为空的时候就退出循环,最后将msg接到prev的屁股后面,即这段代码: prev.next = msg; 

        当然,需要注意到,在其中定义了一个IdleHandler接口,该接口只有一个方法申明:boolean queueIdle()。MessageQueue类中addIdleHandler(IdleHandler handler)函数用于添加队列为空时的IdleHandler。当队列为空时,需要一一通知这些IdleHandler。

4、Looper

--该类用来为一个线程维护一个消息队列
--默认的线程是没有和一个Looper实例关联的,需要在线程中使用Loop.prepare();关联一个线程,使用Looper.loop()处理消息直到结束。

        Looper扮演着一个Handler和 消息队列之间通讯桥梁的角色。程序组件首先通过Handler把 消息传递给Looper,Looper把 消息放入队列。Looper也 把消息队列里的消息广播给所有的Handler,Handler接 受到消息后调用handleMessage进 行处理。
1)   可以通过Looper类 的静态方法Looper.myLooper得 到当前线程的Looper实 例,如果当前线程未关联一个Looper实 例,该方法将返回空。
2)   可以通过静态方法Looper. getMainLooper方法得到主线程的Looper实 例。

[java]  view plain copy
  1. /** 
  2.   * Class used to run a message loop for a thread.  Threads by default do 
  3.   * not have a message loop associated with them; to create one, call 
  4.   * {@link #prepare} in the thread that is to run the loop, and then 
  5.   * {@link #loop} to have it process messages until the loop is stopped. 
  6.   *  
  7.   * 

    Most interaction with a message loop is through the 

  8.   * {@link Handler} class. 
  9.   *  
  10.   * 

    This is a typical example of the implementation of a Looper thread, 

  11.   * using the separation of {@link #prepare} and {@link #loop} to create an 
  12.   * initial Handler to communicate with the Looper. 
  13.   *  
  14.   * 
     
  15.   *  class LooperThread extends Thread { 
  16.   *      public Handler mHandler; 
  17.   *      public void run() { 
  18.   *          Looper.prepare(); 
  19.   *          mHandler = new Handler() { 
  20.   *              public void handleMessage(Message msg) { 
  21.   *                  // process incoming messages here 
  22.   *              } 
  23.   *          }; 
  24.   *           
  25.   *          Looper.loop(); 
  26.   *      } 
  27.   *  } 
  28.   */  

[java]  view plain copy
  1. public class Looper {  
  2.     private static final boolean DEBUG = false;  
  3.     private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;  
  4.   
  5.     // sThreadLocal.get() will return null unless you've called prepare().  
  6.     private static final ThreadLocal sThreadLocal = new ThreadLocal();  
  7.   
  8.     final MessageQueue mQueue;  
  9.     volatile boolean mRun;  
  10.     Thread mThread;  
  11.     private Printer mLogging = null;  
  12.     private static Looper mMainLooper = null;  
  13.      /** Initialize the current thread as a looper. 
  14.       * This gives you a chance to create handlers that then reference 
  15.       * this looper, before actually starting the loop. Be sure to call 
  16.       * {@link #loop()} after calling this method, and end it by calling 
  17.       * {@link #quit()}. 
  18.       */  
  19.     public static final void prepare() {  
  20.         if (sThreadLocal.get() != null) {  
  21.             throw new RuntimeException("Only one Looper may be created per thread");  
  22.         }  
  23.         sThreadLocal.set(new Looper());  
  24. }  
  25. /** Initialize the current thread as a looper, marking it as an application's main  
  26.      *  looper. The main looper for your application is created by the Android environment, 
  27.      *  so you should never need to call this function yourself. 
  28.      * {@link #prepare()} 
  29.      */  
  30.        
  31.     public static final void prepareMainLooper() {  
  32.         prepare();  
  33.         setMainLooper(myLooper());  
  34.         if (Process.supportsProcesses()) {  
  35.             myLooper().mQueue.mQuitAllowed = false;  
  36.         }  
  37.     }  
  38.   
  39.   
  40.     private synchronized static void setMainLooper(Looper looper) {  
  41.         mMainLooper = looper;  
  42.     }  
  43.       
  44.     /** Returns the application's main looper, which lives in the main thread of the application. 
  45.      */  
  46.     public synchronized static final Looper getMainLooper() {  
  47.         return mMainLooper;  
  48.     }  
  49. public static final void loop() {  
  50.         Looper me = myLooper();  
  51.         MessageQueue queue = me.mQueue;  
  52.         while (true) {  
  53.             Message msg = queue.next(); // might block  
  54.             if (msg != null) {  
  55.                 if (msg.target == null) {  
  56.                     return;  
  57.                 }  
  58.                 if (me.mLogging!= null) me.mLogging.println(  
  59.                         ">>>>> Dispatching to " + msg.target + " "  
  60.                         + msg.callback + ": " + msg.what  
  61.                         );  
  62.                 msg.target.dispatchMessage(msg);  
  63.                 if (me.mLogging!= null) me.mLogging.println(  
  64.                         "<<<<< Finished to    " + msg.target + " "  
  65.                         + msg.callback);  
  66.                 msg.recycle();  
  67.             }  
  68.         }  
  69.     }  
  70. /** 
  71.      * Return the Looper object associated with the current thread.  Returns 
  72.      * null if the calling thread is not associated with a Looper. 
  73.      */  
  74.     public static final Looper myLooper() {  
  75.         return (Looper)sThreadLocal.get();  
  76.     }  
        从源码可以看出Looper 封装的信息:
       Looper实质上是对当前线程, ThreadLocal,MessageQueue的封装,也就是负责在多线程之间传递消息的一个循环器.

       当你往Handler中添加消息的时候则,里面这个方法: public static final void loop()死循环的方法就会被系统调用,之后的功能代码是: 
msg.target.dispatchMessage(msg),则从MessageQueue中得到一个Message(msg),之后调用Handler的dispatchMessage(msg),这个方法内部实际调用的就是 Handler.handleMessage(msg)方法,这个就是我们在activity要重写的方法,所以我们就能够得到其他子线程传递的Message了.

你可能感兴趣的:(android,Java,ui,android)