Android应用程序消息处理机制(Looper、Handler)分析(5)

简介:

      ActivityThread类的这个mH成员变量是什么时候创建的呢?我们前面在分析应用程序的消息循环时,说到当应用程序进程启动之后,就会加载ActivityThread类的main函数里面,在这个main函数里面,在通过Looper类进入消息循环之前,会在当前进程中创建一个ActivityThread实例:

  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     public static final void main(String[] args) {  
  5.         ......  
  6.   
  7.         ActivityThread thread = new ActivityThread();  
  8.         thread.attach(false);  
  9.   
  10.         ......  
  11.     }  
  12. }  

 在创建这个实例的时候,就会同时创建其成员变量mH了:

  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     final H mH = new H();  
  5.   
  6.     ......  
  7. }   

  前面说过,H类继承于Handler类,因此,当创建这个H对象时,会调用Handler类的构造函数,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中:

  1. public class Handler {  
  2.     ......  
  3.   
  4.     public Handler() {  
  5.         ......  
  6.   
  7.         mLooper = Looper.myLooper();  
  8.         ......  
  9.   
  10.         mQueue = mLooper.mQueue;  
  11.         ......  
  12.     }  
  13.   
  14.   
  15.     final MessageQueue mQueue;  
  16.     final Looper mLooper;  
  17.     ......  
  18. }  

   在Hanlder类的构造函数中,主要就是初始成员变量mLooper和mQueue了。这里的myLooper是Looper类的静态成员函数,通过它来获得一个Looper对象,这个Looper对象就是前面我们在分析消息循环时,在ActivityThread类的main函数中通过Looper.prepareMainLooper函数创建的。Looper.myLooper函数实现在frameworks/base/core/java/android/os/Looper.java文件中:

  1. public class Looper {  
  2.     ......  
  3.   
  4.     public static final Looper myLooper() {  
  5.         return (Looper)sThreadLocal.get();  
  6.     }  
  7.   
  8.     ......  
  9. }  

  有了这个Looper对象后,就可以通过Looper.mQueue来访问应用程序的消息队列了。

 

        有了这个Handler对象mH后,就可以通过它来往应用程序的消息队列中加入新的消息了。回到前面的queueOrSendMessage函数中,当它准备好了一个Message对象msg后,就开始调用mH.sendMessage函数来发送消息了,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中:

  1. public class Handler {  
  2.     ......  
  3.   
  4.     public final boolean sendMessage(Message msg)  
  5.     {  
  6.         return sendMessageDelayed(msg, 0);  
  7.     }  
  8.   
  9.     public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  10.     {  
  11.         if (delayMillis < 0) {  
  12.             delayMillis = 0;  
  13.         }  
  14.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  15.     }  
  16.   
  17.     public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  18.     {  
  19.         boolean sent = false;  
  20.         MessageQueue queue = mQueue;  
  21.         if (queue != null) {  
  22.             msg.target = this;  
  23.             sent = queue.enqueueMessage(msg, uptimeMillis);  
  24.         }  
  25.         else {  
  26.             ......  
  27.         }  
  28.         return sent;  
  29.     }  
  30.   
  31.     ......  
  32. }  

 

 

   在发送消息时,是可以指定消息的处理时间的,但是通过sendMessage函数发送的消息的处理时间默认就为当前时间,即表示要马上处理,因此,从sendMessage函数中调用sendMessageDelayed函数,传入的时间参数为0,表示这个消息不要延时处理,而在sendMessageDelayed函数中,则会先获得当前时间,然后加上消息要延时处理的时间,即得到这个处理这个消息的绝对时间,然后调用sendMessageAtTime函数来把消息加入到应用程序的消息队列中去。

 

        在sendMessageAtTime函数,首先得到应用程序的消息队列mQueue,这是在Handler对象构造时初始化好的,前面已经分析过了,接着设置这个消息的目标对象target,即这个消息最终是由谁来处理的:

 
 
  1. msg.target = this;   

 

   这里将它赋值为this,即表示这个消息最终由这个Handler对象来处理,即由ActivityThread对象的mH成员变量来处理。

 

 

 

        函数最后调用queue.enqueueMessage来把这个消息加入到应用程序的消息队列中去,这个函数实现在frameworks/base/core/java/android/os/MessageQueue.java文件中:

  1. public class MessageQueue {  
  2.     ......  
  3.   
  4.     final boolean enqueueMessage(Message msg, long when) {  
  5.         ......  
  6.   
  7.         final boolean needWake;  
  8.         synchronized (this) {  
  9.             ......  
  10.   
  11.             msg.when = when;  
  12.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  13.             Message p = mMessages;  
  14.             if (p == null || when == 0 || when < p.when) {  
  15.                 msg.next = p;  
  16.                 mMessages = msg;  
  17.                 needWake = mBlocked; // new head, might need to wake up  
  18.             } else {  
  19.                 Message prev = null;  
  20.                 while (p != null && p.when <= when) {  
  21.                     prev = p;  
  22.                     p = p.next;  
  23.                 }  
  24.                 msg.next = prev.next;  
  25.                 prev.next = msg;  
  26.                 needWake = false// still waiting on head, no need to wake up  
  27.             }  
  28.   
  29.         }  
  30.         if (needWake) {  
  31.             nativeWake(mPtr);  
  32.         }  
  33.         return true;  
  34.     }  
  35.   
  36.     ......  
  37. }  

 

   把消息加入到消息队列时,分两种情况,一种当前消息队列为空时,这时候应用程序的主线程一般就是处于空闲等待状态了,这时候就要唤醒它,另一种情况是应用程序的消息队列不为空,这时候就不需要唤醒应用程序的主线程了,因为这时候它一定是在忙着处于消息队列中的消息,因此不会处于空闲等待的状态。

 第一种情况比较简单,只要把消息放在消息队列头就可以了:

  1. msg.next = p;  
  2. mMessages = msg;  
  3. needWake = mBlocked; // new head, might need to wake up  

第二种情况相对就比较复杂一些了,前面我们说过,当往消息队列中发送消息时,是可以指定消息的处理时间的,而消息队列中的消息,就是按照这个时间从小到大来排序的,因此,当把新的消息加入到消息队列时,就要根据它的处理时间来找到合适的位置,然后再放进消息队列中去:

  1. Message prev = null;  
  2. while (p != null && p.when <= when) {  
  3.     prev = p;  
  4.     p = p.next;  
  5. }  
  6. msg.next = prev.next;  
  7. prev.next = msg;  
  8. needWake = false// still waiting on head, no need to wake up  

把消息加入到消息队列去后,如果应用程序的主线程正处于空闲等待状态,就需要调用natvieWake函数来唤醒它了,这是一个JNI方法,定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

  1. static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {  
  2.     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);  
  3.     return nativeMessageQueue->wake();  
  4. }  

这个JNI层的NativeMessageQueue对象我们在前面分析消息循环的时候创建好的,保存在Java层的MessageQueue对象的mPtr成员变量中,这里把它取回来之后,就调用它的wake函数来唤醒应用程序的主线程,这个函数也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

  1. void NativeMessageQueue::wake() {  
  2.     mLooper->wake();  
  3. }  

 这里它又通过成员变量mLooper的wake函数来执行操作,这里的mLooper成员变量是一个C++层实现的Looper对象,它定义在frameworks/base/libs/utils/Looper.cpp文件中:

  1. void Looper::wake() {  
  2.     ......  
  3.   
  4.     ssize_t nWrite;  
  5.     do {  
  6.         nWrite = write(mWakeWritePipeFd, "W"1);  
  7.     } while (nWrite == -1 && errno == EINTR);  
  8.   
  9.     .......  
  10. }  

  这个wake函数很简单,只是通过打开文件描述符mWakeWritePipeFd往管道的写入一个"W"字符串。其实,往管道写入什么内容并不重要,往管道写入内容的目的是为了唤醒应用程序的主线程。前面我们在分析应用程序的消息循环时说到,当应用程序的消息队列中没有消息处理时,应用程序的主线程就会进入空闲等待状态,而这个空闲等待状态就是通过调用这个Looper类的pollInner函数来进入的,具体就是在pollInner函数中调用epoll_wait函数来等待管道中有内容可读的。

 

        这时候既然管道中有内容可读了,应用程序的主线程就会从这里的Looper类的pollInner函数返回到JNI层的nativePollOnce函数,最后返回到Java层中的MessageQueue.next函数中去,这里它就会发现消息队列中有新的消息需要处理了,于就会处理这个消息。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966601,如需转载请自行联系原作者
目录
相关文章
|
18天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
26天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
50 15
Android 系统缓存扫描与清理方法分析
|
27天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
48 5
|
28天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
1月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
48 1
|
1月前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
45 2
|
4天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
9天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
11天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
13天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。