Android应用程序线程消息循环模型分析(1)

简介:

      我们知道,Android应用程序是通过消息来驱动的,即在应用程序的主线程(UI线程)中有一个消息循环,负责处理消息队列中的消息。我们也知道,Android应用程序是支持多线程的,即可以创建子线程来执行一些计算型的任务,那么,这些子线程能不能像应用程序的主线程一样具有消息循环呢?这些子线程又能不能往应用程序的主线程中发送消息呢?本文将分析Android应用程序线程消息处理模型,为读者解答这两个问题

        在开发Android应用程序中,有时候我们需要在应用程序中创建一些常驻的子线程来不定期地执行一些不需要与应用程序界面交互的计算型的任务。如果这些子线程具有消息循环,那么它们就能够常驻在应用程序中不定期的执行一些计算型任务了:当我们需要用这些子线程来执行任务时,就往这个子线程的消息队列中发送一个消息,然后就可以在子线程的消息循环中执行我们的计算型任务了。我们在前面一篇文章Android系统默认Home应用程序(Launcher)的启动过程源代码分析中,介绍Launcher的启动过程时,在Step 15(LauncherModel.startLoader)中,Launcher就是通过往一个子线程的消息队列中发送一个消息(sWorker.post(mLoaderTask)),然后子线程就会在它的消息循环中处理这个消息的时候执行从PackageManagerService中获取系统中已安装应用程序的信息列表的任务,即调用Step 16中的LoaderTask.run函数。

        在开发Android应用程序中,有时候我们又需要在应用程序中创建一些子线程来执行一些需要与应用程序界面进交互的计算型任务。典型的应用场景是当我们要从网上下载文件时,为了不使主线程被阻塞,我们通常创建一个子线程来负责下载任务,同时,在下载的过程,将下载进度以百分比的形式在应用程序的界面上显示出来,这样就既不会阻塞主线程的运行,又能获得良好的用户体验。但是,我们知道,Android应用程序的子线程是不可以操作主线程的UI的,那么,这个负责下载任务的子线程应该如何在应用程序界面上显示下载的进度呢?如果我们能够在子线程中往主线程的消息队列中发送消息,那么问题就迎刃而解了,因为发往主线程消息队列的消息最终是由主线程来处理的,在处理这个消息的时候,我们就可以在应用程序界面上显示下载进度了。

        上面提到的这两种情况,Android系统都为我们提供了完善的解决方案,前者可以通过使用HandlerThread类来实现,而后者可以使用AsyncTask类来实现,本文就详细这两个类是如何实现的。不过,为了更好地理解HandlerThread类和AsyncTask类的实现,我们先来看看应用程序的主线程的消息循环模型是如何实现的。

        1. 应用程序主线程消息循环模型

        在前面一篇文章Android应用程序进程启动过程的源代码分析一文中,我们已经分析应用程序进程(主线程)的启动过程了,这里主要是针对它的消息循环模型作一个总结。当运行在Android应用程序框架层中的ActivityManagerService决定要为当前启动的应用程序创建一个主线程的时候,它会在ActivityManagerService中的startProcessLocked成员函数调用Process类的静态成员函数start为当前应用程序创建一个主线程:

  1. public final class ActivityManagerService extends ActivityManagerNative      
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {      
  3.       
  4.     ......      
  5.       
  6.     private final void startProcessLocked(ProcessRecord app,      
  7.                 String hostingType, String hostingNameStr) {      
  8.       
  9.         ......      
  10.       
  11.         try {      
  12.             int uid = app.info.uid;      
  13.             int[] gids = null;      
  14.             try {      
  15.                 gids = mContext.getPackageManager().getPackageGids(      
  16.                     app.info.packageName);      
  17.             } catch (PackageManager.NameNotFoundException e) {      
  18.                 ......      
  19.             }      
  20.                   
  21.             ......      
  22.       
  23.             int debugFlags = 0;      
  24.                   
  25.             ......      
  26.                   
  27.             int pid = Process.start("android.app.ActivityThread",      
  28.                 mSimpleProcessManagement ? app.processName : null, uid, uid,      
  29.                 gids, debugFlags, null);      
  30.                   
  31.             ......      
  32.       
  33.         } catch (RuntimeException e) {      
  34.                   
  35.             ......      
  36.       
  37.         }      
  38.     }      
  39.       
  40.     ......      
  41.       
  42. }      

        这里我们主要关注Process.start函数的第一个参数“android.app.ActivityThread”,它表示要在当前新建的线程中加载android.app.ActivityThread类,并且调用这个类的静态成员函数main作为应用程序的入口点。ActivityThread类定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

  1. public final class ActivityThread {    
  2.     ......    
  3.     
  4.     public static final void main(String[] args) {    
  5.         ......  
  6.     
  7.         Looper.prepareMainLooper();    
  8.            
  9.         ......    
  10.     
  11.         ActivityThread thread = new ActivityThread();    
  12.         thread.attach(false);    
  13.     
  14.         ......   
  15.         Looper.loop();    
  16.     
  17.         ......   
  18.     
  19.         thread.detach();    
  20.         ......    
  21.     }    
  22.     
  23.     ......    
  24. }    

        在这个main函数里面,除了创建一个ActivityThread实例外,就是在进行消息循环了。

 

        在进行消息循环之前,首先会通过Looper类的静态成员函数prepareMainLooper为当前线程准备一个消息循环对象。Looper类定义在frameworks/base/core/java/android/os/Looper.java文件中:

  1. public class Looper {  
  2.     ......  
  3.   
  4.     // sThreadLocal.get() will return null unless you've called prepare().  
  5.     private static final ThreadLocal sThreadLocal = new ThreadLocal();  
  6.   
  7.     ......  
  8.   
  9.     private static Looper mMainLooper = null;  
  10.   
  11.     ......  
  12.   
  13.     public static final void prepare() {  
  14.         if (sThreadLocal.get() != null) {  
  15.             throw new RuntimeException("Only one Looper may be created per thread");  
  16.         }  
  17.         sThreadLocal.set(new Looper());  
  18.     }  
  19.   
  20.     ......  
  21.   
  22.     public static final void prepareMainLooper() {  
  23.         prepare();  
  24.         setMainLooper(myLooper());  
  25.         ......  
  26.     }  
  27.   
  28.     private synchronized static void setMainLooper(Looper looper) {  
  29.         mMainLooper = looper;  
  30.     }  
  31.   
  32.     public synchronized static final Looper getMainLooper() {  
  33.         return mMainLooper;  
  34.     }  
  35.   
  36.     ......  
  37.   
  38.     public static final Looper myLooper() {  
  39.         return (Looper)sThreadLocal.get();  
  40.     }  
  41.   
  42.     ......  
  43. }  

 

        Looper类的静态成员函数prepareMainLooper是专门应用程序的主线程调用的,应用程序的其它子线程都不应该调用这个函数来在本线程中创建消息循环对象,而应该调用prepare函数来在本线程中创建消息循环对象,下一节我们介绍一个线程类HandlerThread 时将会看到。

        为什么要为应用程序的主线程专门准备一个创建消息循环对象的函数呢?这是为了让其它地方能够方便地通过Looper类的getMainLooper函数来获得应用程序主线程中的消息循环对象。获得应用程序主线程中的消息循环对象又有什么用呢?一般就是为了能够向应用程序主线程发送消息了。

        在prepareMainLooper函数中,首先会调用prepare函数在本线程中创建一个消息循环对象,然后将这个消息循环对象放在线程局部变量sThreadLocal中:

  1. sThreadLocal.set(new Looper());  

        接着再将这个消息循环对象通过调用setMainLooper函数来保存在Looper类的静态成员变量mMainLooper中:

  1. mMainLooper = looper;  

       这样,其它地方才可以调用getMainLooper函数来获得应用程序主线程中的消息循环对象。

 

       消息循环对象创建好之后,回到ActivityThread类的main函数中,接下来,就是要进入消息循环了:

  1. Looper.loop();   

        Looper类具体是如何通过loop函数进入消息循环以及处理消息队列中的消息,可以参考前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析,这里就不再分析了,我们只要知道ActivityThread类中的main函数执行了这一步之后,就为应用程序的主线程准备好消息循环就可以了。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966875,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
190 4
|
2月前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
27天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
56 14
|
30天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
1月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
30 8
|
28天前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
1月前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
56 4
|
3月前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
91 15
Android 系统缓存扫描与清理方法分析
|
2月前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
41 1
|
3月前
|
并行计算 JavaScript 前端开发
单线程模型
【10月更文挑战第15天】