您的位置:首页 > 移动开发 > Android开发

android 消息机制 Handler Looper 原理分析

2011-12-02 16:12 561 查看
android消息机制总结

1. 使用场景

Android启动后,会创建一个进程,以后所有的activity、service都在这个进程里面运行。启动初,该进程只包含一个线程,叫做UI主线程,负责处理UI界面的显示、更新。对于一些费时的操作,如查询、下载数据等不可以放到UI线程中,因为这可能会引起UI线程阻塞,导致系统崩溃。所以,这些费时操作需要单独启动一个子线程去处理。子线程处理完毕将结果通知给UI主线程,主线程得到结果后更新UI界面。子线程如何与UI主线程进行通信?在android中使用了消息机制来完成线程之间的通信。



2. 场景分析

如何使用消息机制来解决上面的问题?假设UI线程有一个自己的消息队列,简称UI_MQ,UI_MQ用于存储UI线程需要处理的消息。子线程需要与UI线程通信时将消息发送到UI_MQ中即可。完成这些操作需要解决下面的问题:

(1) 为UI线程创建一个唯一的消息队列UI_MQ

(2) 子线程将消息发送到UI线程对应的消息队列UI_MQ

(3) UI线程循环查询UI_MQ是否有消息,如果有取出处理。

解决上面三个问题不难,对应的方法如下:

(1) 创建一个链表作为UI线程消息队列保存消息

(2) 子线程持有对UI_MQ的引用,便可以将消息加入到UI_MQ

(3) UI线程设置一个循环等待方法,用于处理UI_MQ。每当发现有消息存在,取出处理。

上面描述了消息推送的基本实现,如果设计一个好的消息通信框架,需要对此进一步完善,下面看android中的消息机制是怎么实现的。

3. 整体架构

Anroid中消息机制包括下面几个概念:对应的类图如图3.1所示。






(1) Thread 线程

(2) Message 消息

(3) MessageQueue 消息队列,每个线程里有唯一一个消息队列。

(4) MessagePool 消息池。

(5) Looper 循环查询消息队列,每个线程里有唯一一个消息循环。

(6) Handler 处理消息。作用有创建消息、发送消息、处理消息。

(7) ThreadLocal 保证每个线程只创建唯一一个Looper和MessageQueue。

“场景分析”提到,子线程持有UI线程消息队列UI_MQ的引用便可以将消息发送到UI_MQ中。Android中不是这么实现,android通过Handler创建消息、发送消息、处理消息。Handler是如何完成这些功能的呢?

Handler创建消息

每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。

//Handler.java
//Handler构造时会获取Looper对象,这个对象是单例的,下面有介绍,并获取Looper对象对应的消息队列
public Handler() {  
        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 leaks might 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 = null;  
    }
//可以通过Handler创建消息,并将消息发送到Handler所在线程对应的消息队列中。以后,Looper调用looper()函数后会循环遍历消息队列处理消息
    public final boolean sendMessageAtFrontOfQueue(Message msg)  
    {  
         boolean sent = false;  
         MessageQueue queue = mQueue;  
         if (queue != null) {  
             msg.target = this;  
             sent = queue.enqueueMessage(msg, 0);  
         }  
         else {  
             RuntimeException e = new RuntimeException(  
                 this + " sendMessageAtTime() called with no mQueue");  
             Log.w("Looper", e.getMessage(), e);  
         }  
         return sent;  
     }




消息的创建流程如图3.2所示。




Handler发送消息

UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。

//Looper.java
public static final void prepare() {  
       //如果之前有过初始化,直接抛异常。ThreadLocal的作用就是保证每个线程最多创建一个Looper对象
if (sThreadLocal.get() != null) {  
             throw new RuntimeException("Only one Looper may be created per thread");  
         }  
         sThreadLocal.set(new Looper());  
     }
//调用静态方法loop()以后,便开始了对消息队列的遍历
    public static final void loop() {  
         Looper me = myLooper();  
         MessageQueue queue = me.mQueue;  
         while (true) {  
             Message msg = queue.next(); // might block  
             //if (!me.mRun) {  
             //    break;  
         //}  
             if (msg != null) {  
                 if (msg.target == null) {  
                     // No target is a magic identifier for the quit message.  
                     return;  
                 }  
                 if (me.mLogging!= null) me.mLogging.println(  
                         ">>>>> Dispatching to " + msg.target + " " 
                         + msg.callback + ": " + msg.what  
                        );  
                 msg.target.dispatchMessage(msg);  
                 if (me.mLogging!= null) me.mLogging.println(  
                         "<<<<< Finished to    " + msg.target + " " 
                         + msg.callback);  
                 msg.recycle();  
             }  
         }  
     }  

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




Handler、Looper、MessageQueue的初始化流程如图3.3所示。



Hander持有对UI主线程消息队列UI_MQ和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列UI_MQ中。

Handler处理消息

UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

子线程通过Handler、Looper与UI主线程通信的流程如图3.4所示。




图 3.4 子线程通过Handler Looper与UI主线程进行通信

//Handler.java
public void dispatchMessage(Message msg) {  
         if (msg.callback != null) {  
             handleCallback(msg);  
         } else {  
             if (mCallback != null) {  
                 if (mCallback.handleMessage(msg)) {  
                     return;  
                 }  
             }  
             handleMessage(msg);  //具体处理由对应的Handler实现
         }  
     }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: