当前位置: 代码迷 >> 综合 >> android 消息机制 Handler Looper 原理分析
  详细解决方案

android 消息机制 Handler Looper 原理分析

热度:77   发布时间:2023-12-08 12:02:02.0

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实现
}  
}  


 

  相关解决方案