Handler消息机制浅谈

来源:互联网 发布:五毒风湿骨刺丹,淘宝网 编辑:程序博客网 时间:2024/04/29 12:43

Message:消息;其中包含了消息ID,消息对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理

Handler:处理者;负责Message发送消息及处理。Handler通过与Looper进行沟通,从而使用Handler时,需要实现handlerMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等(主线程中才行)

MessageQueue:消息队列;用来存放Handler发送过来的消息,并按照FIFO(先入先出队列)规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等Looper的抽取。

Looper:消息泵,不断从MessageQueue中抽取Message执行。因此,一个线程中的MessageQueue需要一个Looper进行管理。Looper是当前线程创建的时候产生的(UI Thread即主线程是系统帮忙创建的Looper,而如果在子线程中,需要手动在创建线程后立即创建Looper[调用Looper.prepare()方法])。也就是说,会在当前线程上绑定一个Looper对象。

Thread:线程;负责调度消息循环,即消息循环的执行场所。

知识要点 

一、说明

1、handler应该由处理消息的线程创建。

2、handler与创建它的线程相关联,而且也只与创建它的线程相关联。handler运行在创建它的线程中,所以,如果在handler中进行耗时的操作,会阻塞创建它的线程。

二、一些知识点

1、Android的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper。主线程(UI线程)就是一个消息循环的线程。

2、获取looper

Looper.myLooper();     //获得当前的Looper

Looper.getMainLooper() //获得UI线程的Lopper

3、Handle的初始化函数(构造函数),如果没有参数,那么他就默认使用的是当前的Looper,如果有Looper参数,就是用对应的线程的Looper。

4、如果一个线程中调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用 Looper.loop();之后就进入了消息循环,这个之后就可以发消息、取消息、和处理消息。

消息处理机制原理:

在创建Activity之前,当系统启动的时候,先加载ActivityThread这个类,在这个类中的main函数,调用了Looper.prepareMainLooper();方法进行初始化Looper对象;然后创建了主线程的handler对象(Tips:加载ActivityThread的时候,其内部的Handler对象[静态的]还未创建);随后才创建了ActivityThread对象;最后调用了Looper.loop();方法,不断的进行轮询消息队列的消息。也就是说,在ActivityThread和Activity创建之前(同样也是Handler创建之前,当然handler由于这两者初始化),就已经开启了Looper的loop()方法,不断的进行轮询消息。需要注意的是,这个轮询的方法是阻塞式的,没有消息就一直等待(实际是等着MessageQueue的next()方法返回消息)。在应用一执行的时候,就已经开启了Looper,并初始化了Handler对象。此时,系统的某些组件或者其他的一些活动等发送了系统级别的消息,这个时候主线程中的Looper就可以进行轮询消息,并调用msg.target.dispatchMessage(msg)(msg.target即为handler)进行分发消息,并通过handler的handleMessage方法进行处理;所以会优于我们自己创建的handler中的消息而处理系统消息。

 

0、准备数据和对象:

①、如果在主线程中处理message(即创建handler对象),那么如上所述,系统的Looper已经准备好了(当然,MessageQueue也初始化了),且其轮询方法loop已经开启。【系统的Handler准备好了,是用于处理系统的消息】。【Tips:如果是子线程中创建handler,就需要显式的调用Looper的方法prepare()和loop(),初始化Looper和开启轮询器】

②、通过Message.obtain()准备消息数据(实际是从消息池中取出的消息)

③、创建Handler对象,在其构造函数中,获取到Looper对象、MessageQueue对象(从Looper中获取的),并将handler作为message的标签设置到msg.target上

1、发送消息:sendMessage():通过Handler将消息发送给消息队列

2、给Message贴上handler的标签:在发送消息的时候,为handler发送的message贴上当前handler的标签

3、开启HandlerThread线程,执行run方法。

4、在HandlerThread类的run方法中开启轮询器进行轮询:调用Looper.loop()方法进行轮询消息队列的消息

Tips:这两步需要再斟酌,个人认为这个类是自己手动创建的一个线程类,Looper的开启在上面已经详细说明了,这里是说自己手动创建线程(HandlerThread)的时候,才会在这个线程中进行Looper的轮询的】

5、在消息队列MessageQueue中enqueueMessage(Message msg, long when)方法里,对消息进行入列,即依据传入的时间进行消息入列(排队)

6、轮询消息:与此同时,Looper在不断的轮询消息队列

7、在Looper.loop()方法中,获取到MessageQueue对象后,从中取出消息(Message msg = queue.next()

8、分发消息:从消息队列中取出消息后,调用msg.target.dispatchMessage(msg);进行分发消息

9、将处理好的消息分发给指定的handler处理,即调用了handler的dispatchMessage(msg)方法进行分发消息。

10、在创建handler时,复写的handleMessage方法中进行消息的处理

11、回收消息:在消息使用完毕后,在Looper.loop()方法中调用msg.recycle(),将消息进行回收,即将消息的所有字段恢复为初始状态。


二、详细解释:

1、准备Looper对象

两种情况初始化Looper对象:

1)在主线程中不需要显式的创建Looper对象,直接创建Handler对象即可;因为在主线程ActivityThread的main函数中已经自动调用了创建Looper的方法:Looper.prepareMainLooper();,并在最后调用了Looper.loop()方法进行轮询。

2)如果在子线程中创建Handler对象,需要创建Looper对象,即调用显式的调用Looper.prepare()

初始化Looper的工作:

1)初始化Looper对象:通过调用Looper.prepare()初始化Looper对象,在这个方法中,新创建了Looper对象;在创建Looper的同时,(在其构造函数中)也初始化了MessageQueue。

2)将Looper绑定到当前线程:在初始化中,调用sThreadLocal.set(new Looper(quitAllowed))方法,将其和ThreadLocal进行绑定

在ThreadLocal对象中的set方法,是将当前线程和Looper绑定到一起:首先获取到当前的线程,并获取线程内部类Values,通过Thread.Values的put方法,将当前线程和Looper对象进行绑定到一起。即将传入的Looper对象挂载到当前线程上。

Tips:在Looper对象中,可以通过getThread()方法,获取到当前线程,即此Looper绑定的线程对象。

源代码:

Looper中:

[java] view plain copy
 print?
  1. public static void prepare() {  
  2.         prepare(true);  
  3.     }  
  4.     private static void prepare(boolean quitAllowed) {  
  5.         if (sThreadLocal.get() != null) {  
  6.             throw new RuntimeException("Only one Looper may be created per thread");  
  7.         }  
  8.         sThreadLocal.set(new Looper(quitAllowed));  
  9.     }  
  10. private Looper(boolean quitAllowed) {  
  11.         mQueue = new MessageQueue(quitAllowed);  
  12.         mRun = true;  
  13.         mThread = Thread.currentThread();  
  14.     }  

ThreadLocal中:

[java] view plain copy
 print?
  1. public void set(T value) {  
  2.         Thread currentThread = Thread.currentThread();  
  3.         Values values = values(currentThread);  
  4.         if (values == null) {  
  5.             values = initializeValues(currentThread);  
  6.         }  
  7.         values.put(this, value);  
  8.     }  

2、创建消息Message:

消息的创建可以通过两种方式:

1)new Message()

2)Message.obtain():【当存在多个handler的时候,可以通过Message.obtain(Handler handler)创建消息,指定处理的handler对象】

Tips:建议使用第二种方式更好一些。原因:

       因为通过第一种方式,每有一个新消息,都要进行new一个Message对象,这会创建出多个Message,很占内存。

       而如果通过obtain的方法,是从消息池sPool中取出消息。每次调用obtain()方法的时候,先判断消息池是否有消息(if (sPool != null)),没有则创建新消息对象,有则从消息池中取出消息,并将取出的消息从池中移除【具体看obtain()方法】

[java] view plain copy
 print?
  1. public static Message obtain() {  
  2.         synchronized (sPoolSync) {  
  3.             if (sPool != null) {  
  4.                 Message m = sPool;  
  5.                 sPool = m.next;  
  6.                 m.next = null;  
  7.                 sPoolSize--;  
  8.                 return m;  
  9.             }  
  10.         }  
  11.         return new Message();  
  12.     }  
  13.   
  14. public Message() {  
  15.     }  

3、创建Handler对象

两种形式创建Handler对象:

1)创建无参构造函数的Handler对象:

2)创建指定Looper对象的Handler对象

最终都会调用相应的含有Callback和boolean类型的参数的构造函数

【这里的Callback是控制是否分发消息的,其中含有一个返回值为boolean的handleMessage(Message msg)方法进行判断的;

 boolean类型的是参数是判断是否进行异步处理,这个参数默认是系统处理的,我们无需关心】

在这个构造函数中,进行了一系列的初始化工作:

①、获取到当前线程中的Looper对象

②、通过Looper对象,获取到消息队列MessageQueue对象

③、获取Callback回调对象

④、获取异步处理的标记

源代码:

①、创建无参构造函数的Handler对象:

[java] view plain copy
 print?
  1. public Handler() {  
  2.         this(nullfalse);  
  3.     }  
  4. public Handler(Callback callback, boolean async) {  
  5.         if (FIND_POTENTIAL_LEAKS) {  
  6.             final Class<? extends Handler> klass = getClass();  
  7.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {  
  8.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());  
  9.             }  
  10.         }  
  11.         mLooper = Looper.myLooper();  
  12.         if (mLooper == null) {  
  13.             throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");  
  14.         }  
  15.         mQueue = mLooper.mQueue;  
  16.         mCallback = callback;  
  17.         mAsynchronous = async;  
  18.     }  

②、创建指定Looper对象的Handler对象

[java] view plain copy
 print?
  1. public Handler(Looper looper) {  
  2.         this(looper, nullfalse);  
  3.     }  
  4. public Handler(Looper looper, Callback callback, boolean async) {  
  5.         mLooper = looper;  
  6.         mQueue = looper.mQueue;  
  7.         mCallback = callback;  
  8.         mAsynchronous = async;  
  9.     }  

4、Handler对象发送消息:

1)Handler发送消息给消息队列:

Handler对象通过调用sendMessage(Message msg)方法,最终将消息发送给消息队列进行处理

这个方法(所有重载的sendMessage)最终调用的是enqueueMessage(MessageQueuequeue, Message msg, long uptimeMillis)

(1)先拿到消息队列:在调用到sendMessageAtTime(Messagemsg, long uptimeMillis)方法的时候,获取到消息队列(在创建Handler对象时获取到的)

(2)当消息队列不为null的时候(为空直接返回false,告知调用者处理消息失败),再调用处理消息入列的方法:

enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

这个方法,做了三件事:

①、为消息打上标签:msg.target = this;:将当前的handler对象这个标签贴到传入的message对象上,为Message指定处理者

②、异步处理消息:msg.setAsynchronous(true);,在asyn为true的时候设置

③、将消息传递给消息队列MessageQueue进行处理:queue.enqueueMessage(msg, uptimeMillis);

[java] view plain copy
 print?
  1. public final boolean sendMessage(Message msg){  
  2.         return sendMessageDelayed(msg, 0);  
  3.     }  
  4. public final boolean sendMessageDelayed(Message msg, long delayMillis){  
  5.         if (delayMillis < 0) {  
  6.             delayMillis = 0;  
  7.         }  
  8.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  9.     }  
  10. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  11.         MessageQueue queue = mQueue;  
  12.         if (queue == null) {  
  13.             RuntimeException e = new RuntimeException(  
  14.                     this + " sendMessageAtTime() called with no mQueue");  
  15.             Log.w("Looper", e.getMessage(), e);  
  16.             return false;  
  17.         }  
  18.         return enqueueMessage(queue, msg, uptimeMillis);  
  19.     }  
  20. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  21.         msg.target = this;  
  22.         if (mAsynchronous) {  
  23.             msg.setAsynchronous(true);  
  24.         }  
  25.         return queue.enqueueMessage(msg, uptimeMillis);  
  26.     }  

2)MessageQueue消息队列处理消息:

在其中的enqueueMessage(Messagemsg, long when)方法中,工作如下:

在消息未被处理且handler对象不为null的时候,进行如下操作(同步代码块中执行)

①、将传入的处理消息的时间when(即为上面的uptimeMillis)赋值为当前消息的when属性。

②、将next()方法中处理好的消息赋值给新的消息引用:Message p =mMessages;

       在next()方法中:不断的从消息池中取出消息,赋值给mMessage,当没有消息发来的时候,Looper的loop()方法由于是阻塞式的,就一直等消息传进来

③、当传入的时间为0,且next()方法中取出的消息为null的时候,将传入的消息msg入列,排列在消息队列上,此时为消息是先进先出的

       否则,进入到死循环中,不断的将消息入列,根据消息的时刻(when)来排列发送过来的消息,此时消息是按时间的先后进行排列在消息队列上的

[java] view plain copy
 print?
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         if (msg.isInUse()) {  
  3.             throw new AndroidRuntimeException(msg + " This message is already in use.");  
  4.         }  
  5.         if (msg.target == null) {  
  6.             throw new AndroidRuntimeException("Message must have a target.");  
  7.         }  
  8.         boolean needWake;  
  9.         synchronized (this) {  
  10.             if (mQuiting) {  
  11.                 RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");  
  12.                 Log.w("MessageQueue", e.getMessage(), e);  
  13.                 return false;  
  14.             }  
  15.   
  16.             msg.when = when;  
  17.             Message p = mMessages;  
  18.             if (p == null || when == 0 || when < p.when) {  
  19.                 // New head, wake up the event queue if blocked.  
  20.                 msg.next = p;  
  21.                 mMessages = msg;  
  22.                 needWake = mBlocked;  
  23.             } else {  
  24.                 needWake = mBlocked && p.target == null && msg.isAsynchronous();  
  25.                 Message prev;  
  26.                 for (;;) {  
  27.                     prev = p;  
  28.                     p = p.next;  
  29.                     if (p == null || when < p.when) {  
  30.                         break;  
  31.                     }  
  32.                     if (needWake && p.isAsynchronous()) {  
  33.                         needWake = false;  
  34.                     }  
  35.                 }  
  36.                 msg.next = p; // invariant: p == prev.next  
  37.                 prev.next = msg;  
  38.             }  
  39.         }  
  40.         if (needWake) {  
  41.             nativeWake(mPtr);  
  42.         }  
  43.         return true;  
  44.     }  

5、轮询Message

1)开启loop轮询消息

当开启线程的时候,执行run方法,在HandlerThread类中,调用的run方法中将开启loop进行轮询消息队列:

在loop方法中,先拿到MessageQueue对象,然后死循环不断从队列中取出消息,当消息不为null的时候,通过handler分发消息:msg.target.dispatchMessage(msg)。消息分发完之后,调用msg.recycle()回收消息,

2)回收消息:

在Message的回收消息recycle()这个方法中:首先调用clearForRecycle()方法,将消息的所有字段都恢复到原始状态【如flags=0,what=0,obj=null,when=0等等】

然后在同步代码块中将消息放回到消息池sPool中,重新利用Message对象

源代码:

Looper.loop()

[java] view plain copy
 print?
  1. public static void loop() {  
  2.         final Looper me = myLooper();  
  3.         if (me == null) {  
  4.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.         }  
  6.         final MessageQueue queue = me.mQueue;  
  7.         Binder.clearCallingIdentity();  
  8.         final long ident = Binder.clearCallingIdentity();  
  9.         for (;;) {  
  10.             Message msg = queue.next(); // might block  
  11.             if (msg == null) {  
  12.                return;  
  13.             }  
  14.             // This must be in a local variable, in case a UI event sets the logger  
  15.             Printer logging = me.mLogging;  
  16.             if (logging != null) {  
  17.                 logging.println(">>>>> Dispatching to " + msg.target + " " +  
  18.                         msg.callback + ": " + msg.what);  
  19.             }  
  20.             msg.target.dispatchMessage(msg);  
  21.             if (logging != null) {  
  22.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  23.             }  
  24.             final long newIdent = Binder.clearCallingIdentity();  
  25.             if (ident != newIdent) {  
  26.                 Log.wtf(TAG, “……”);  
  27.             }  
  28.             msg.recycle();  
  29.         }  
  30.     }  

msg.recycle();:

[java] view plain copy
 print?
  1. public void recycle() {  
  2.         clearForRecycle();  
  3.         synchronized (sPoolSync) {  
  4.             if (sPoolSize < MAX_POOL_SIZE) {  
  5.                 next = sPool;  
  6.                 sPool = this;  
  7.                 sPoolSize++;  
  8.             }  
  9.         }  
  10.     }  

[java] view plain copy
 print?
  1. /*package*/ void clearForRecycle() {  
  2.         flags = 0;  
  3.         what = 0;  
  4.         arg1 = 0;  
  5.         arg2 = 0;  
  6.         obj = null;  
  7.         replyTo = null;  
  8.         when = 0;  
  9.         target = null;  
  10.         callback = null;  
  11.         data = null;  
  12.     }  

6、处理Message

在Looper.loop()方法中调用了msg.target.dispatchMessage(msg);的方法,就是调用了Handler中的dispatchMessage(Message msg)方法:

1)依据Callback中的handleMessage(msg)的真假判断是否要处理消息,如果是真则不进行消息分发,则不处理消息,否则进行处理消息

2)当Callback为null或其handleMessage(msg)的返回值为false的时候,进行分发消息,即调用handleMessage(msg)处理消息【这个方法需要自己复写】

[java] view plain copy
 print?
  1. /** 
  2.      * Subclasses must implement this to receive messages. 
  3.      */  
  4.     public void handleMessage(Message msg) {  
  5.     }  
  6.       
  7.     /** 
  8.      * Handle system messages here. 
  9.      */  
  10.     public void dispatchMessage(Message msg) {  
  11.         if (msg.callback != null) {  
  12.             handleCallback(msg);  
  13.         } else {  
  14.             if (mCallback != null) {  
  15.                 if (mCallback.handleMessage(msg)) {  
  16.                     return;  
  17.                 }  
  18.             }  
  19.             handleMessage(msg);  
  20.         }  
  21.     }  
原创粉丝点击