`

Android应用程序发送广播(sendBroadcast)的过程分析

 
阅读更多

前面我们分析了Android应用程序注册广播接收器的过程,这个过程只完成了万里长征的第一步,接下来它还要等待ActivityManagerService将广播分发过来。ActivityManagerService是如何得到广播并把它分发出去的呢?这就是本文要介绍的广播发送过程了。

广播的发送过程比广播接收器的注册过程要复杂得多了,不过这个过程仍然是以ActivityManagerService为中心。广播的发送者将广播发送到ActivityManagerService,ActivityManagerService接收到这个广播以后,就会在自己的注册中心查看有哪些广播接收器订阅了该广播,然后把这个广播逐一发送到这些广播接收器中,但是ActivityManagerService并不等待广播接收器处理这些广播就返回了,因此,广播的发送和处理是异步的。概括来说,广播的发送路径就是从发送者到ActivityManagerService,再从ActivityManagerService到接收者,这中间的两个过程都是通过Binder进程间通信机制来完成的,因此,希望读者在继续阅读本文之前,对Android系统的Binder进程间通信机制有所了解,具体可以参考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。

本文继续以Android系统中的广播(Broadcast)机制简要介绍和学习计划一文中所开发的应用程序为例子,并且结合上文Android应用程序注册广播接收器(registerReceiver)的过程分析的内容,一起来分析Android应用程序发送广播的过程。

回顾一下Android系统中的广播(Broadcast)机制简要介绍和学习计划一文中所开发的应用程序的组织架构,MainActivity向ActivityManagerService注册了一个CounterService.BROADCAST_COUNTER_ACTION类型的计数器服务广播接收器,计数器服务CounterService在后台线程中启动了一个异步任务(AsyncTask),这个异步任务负责不断地增加计数,并且不断地将当前计数值通过广播的形式发送出去,以便MainActivity可以将当前计数值在应用程序的界面线程中显示出来。

计数器服务CounterService发送广播的代码如下所示:

在onProgressUpdate函数中,创建了一个BROADCAST_COUNTER_ACTION类型的Intent,并且在这里个Intent中附加上当前的计数器值,然后通过CounterService类的成员函数sendBroadcast将这个Intent发送出去。CounterService类继承了Service类,Service类又继承了ContextWrapper类,成员函数sendBroadcast就是从ContextWrapper类继承下来的,因此,我们就从ContextWrapper类的sendBroadcast函数开始,分析广播发送的过程。

在继承分析广播的发送过程前,我们先来看一下广播发送过程的序列图,然后按照这个序图中的步骤来一步一步分析整个过程。


点击查看大图

Step 1. ContextWrapper.sendBroadcast

这个函数定义在frameworks/base/core/java/android/content/ContextWrapper.java文件中:

这里的成员变量mBase是一个ContextImpl实例,这里只简单地调用ContextImpl.sendBroadcast进一行操作。

Step 2.ContextImpl.sendBroadcast

这个函数定义在frameworks/base/core/java/android/app/ContextImpl.java文件中:

这里的resolvedType表示这个Intent的MIME类型,我们没有设置这个Intent的MIME类型,因此,这里的resolvedType为null。接下来就调用ActivityManagerService的远程接口ActivityManagerProxy把这个广播发送给ActivityManagerService了。

Step 3.ActivityManagerProxy.broadcastIntent

这个函数定义在frameworks/base/core/java/android/app/ActivityManagerNative.java文件中:

这里的实现比较简单,把要传递的参数封装好,然后通过Binder驱动程序进入到ActivityManagerService的broadcastIntent函数中。

Step 4.ctivityManagerService.broadcastIntent

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

这里调用broadcastIntentLocked函数来进一步处理。

Step 5.ActivityManagerService.broadcastIntentLocked

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

这个函数首先是根据intent找出相应的广播接收器:

回忆一下前面一篇文章Android应用程序注册广播接收器(registerReceiver)的过程分析中的Step 6(ActivityManagerService.registerReceiver)中,我们将一个filter类型为BROADCAST_COUNTER_ACTION类型的BroadcastFilter实例保存在了ActivityManagerService的成员变量mReceiverResolver中,这个BroadcastFilter实例包含了我们所注册的广播接收器,这里就通过mReceiverResolver.queryIntent函数将这个BroadcastFilter实例取回来。由于注册一个广播类型的接收器可能有多个,所以这里把所有符合条件的的BroadcastFilter实例放在一个List中,然后返回来。在我们这个场景中,这个List就只有一个BroadcastFilter实例了,就是MainActivity注册的那个广播接收器。

继续往下看:

这里是查看一下这个intent的Intent.FLAG_RECEIVER_REPLACE_PENDING位有没有设置,如果设置了的话,ActivityManagerService就会在当前的系统中查看有没有相同的intent还未被处理,如果有的话,就有当前这个新的intent来替换旧的intent。这里,我们没有设置intent的Intent.FLAG_RECEIVER_REPLACE_PENDING位,因此,这里的replacePending变量为false。

再接着往下看:

前面我们说到,这里得到的列表registeredReceivers的大小为1,且传进来的参数ordered为false,表示要将这个广播发送给所有注册了BROADCAST_COUNTER_ACTION类型广播的接收器,因此,会执行下面的if语句。这个if语句首先创建一个广播记录块BroadcastRecord,里面记录了这个广播是由谁发出的以及要发给谁等相关信息。由于前面得到的replacePending变量为false,因此,不会执行接下来的if语句,即不会检查系统中是否有相同类型的未处理的广播。

这样,这里得到的replaced变量的值也为false,于是,就会把这个广播记录块r放在ActivityManagerService的成员变量mParcelBroadcasts中,等待进一步处理;进一步处理的操作由函数scheduleBroadcastsLocked进行。

Step 6.ActivityManagerService.scheduleBroadcastsLocked

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

这里的mBroadcastsScheduled表示ActivityManagerService当前是不是正在处理其它广播,如果是的话,这里就先不处理直接返回了,保证所有广播串行处理。

注意这里处理广播的方式,它是通过消息循环来处理,每当ActivityManagerService接收到一个广播时,它就把这个广播放进自己的消息队列去就完事了,根本不管这个广播后续是处理的,因此,这里我们可以看出广播的发送和处理是异步的。

这里的成员变量mHandler是一个在ActivityManagerService内部定义的Handler类变量,通过它的sendEmptyMessage函数把一个类型为BROADCAST_INTENT_MSG的空消息放进ActivityManagerService的消息队列中去。这里的空消息是指这个消息除了有类型信息之外,没有任何其它额外的信息,因为前面已经把要处理的广播信息都保存在mParcelBroadcasts中了,等处理这个消息时,从mParcelBroadcasts就可以读回相关的广播信息了,因此,这里不需要把广播信息再放在消息内容中。

Step 7. Handler.sendEmptyMessage

这个自定义的Handler类实现在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中,它是ActivityManagerService的内部类,调用了它的sendEmptyMessage函数来把一个消息放到消息队列后,一会就会调用它的handleMessage函数来真正处理这个消息:

这里又调用了ActivityManagerService的processNextBroadcast函数来处理下一个未处理的广播。

Step 8.ActivityManagerService.processNextBroadcast

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

这里传进来的参数fromMsg为true,于是把mBroadcastScheduled重新设为false,这样,下一个广播就能进入到消息队列中进行处理了。前面我们在Step 5中,把一个广播记录块BroadcastRecord放在了mParallelBroadcasts中,因此,这里就把它取出来进行处理了。广播记录块BroadcastRecord的receivers列表中包含了要接收这个广播的目标列表,即前面我们注册的广播接收器,用BroadcastFilter来表示,这里while循环中的for循环就是把这个广播发送给每一个订阅了该广播的接收器了,通过deliverToRegisteredReceiverLocked函数执行。

Step 9.ActivityManagerService.deliverToRegisteredReceiverLocked

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

函数首先是检查一下广播发送和接收的权限,在我们分析的这个场景中,没有设置权限,因此,这个权限检查就跳过了,这里得到的skip为false,于是进入下面的if语句中。由于上面传时来的ordered参数为false,因此,直接就调用performReceiveLocked函数来进一步执行广播发送的操作了。

Step 10.ActivityManagerService.performReceiveLocked

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

注意,这里传进来的参数app是注册广播接收器的Activity所在的进程记录块,在我们分析的这个场景中,由于是MainActivity调用registerReceiver函数来注册这个广播接收器的,因此,参数app所代表的ProcessRecord就是MainActivity所在的进程记录块了;而参数receiver也是注册广播接收器时传给ActivityManagerService的一个Binder对象,它的类型是IIntentReceiver,具体可以参考上一篇文章Android应用程序注册广播接收器(registerReceiver)的过程分析中的Step 2。

MainActivity在注册广播接收器时,已经把自己的ProcessRecord记录下来了,所以这里的参数app和app.thread均不为null,于是,ActivityManagerService就调用app.thread.scheduleRegisteredReceiver函数来把这个广播分发给MainActivity了。这里的app.thread是一个Binder远程对象,它的类型是ApplicationThreadProxy,我们在前面介绍应用程序的Activity启动过程时,已经多次看到了,具体可以参考主题Android应用程序的Activity启动过程简要介绍和学习计划

Step 11.ApplicationThreadProxy.scheduleRegisteredReceiver
这个函数定义在frameworks/base/core/java/android/app/ApplicationThreadNative.java文件中:

这里通过Binder驱动程序就进入到ApplicationThread.scheduleRegisteredReceiver函数去了。ApplicationThread是ActivityThread的一个内部类,具体可以参考Activity启动主题Android应用程序的Activity启动过程简要介绍和学习计划

Step 12.ApplicaitonThread.scheduleRegisteredReceiver
这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

这里的receiver是在前面一篇文章Android应用程序注册广播接收器(registerReceiver)的过程分析中的Step 4中创建的,它的具体类型是LoadedApk.ReceiverDispatcher.InnerReceiver,即定义在LoadedApk类的内部类ReceiverDispatcher里面的一个内部类InnerReceiver,这里调用它的performReceive函数。

Step 13.InnerReceiver.performReceive

这个函数定义在frameworks/base/core/java/android/app/LoadedApk.java文件中:

这里,它只是简单地调用ReceiverDispatcher的performReceive函数来进一步处理,这里的ReceiverDispatcher类是LoadedApk类里面的一个内部类。

Step 14.ReceiverDispatcher.performReceive

这个函数定义在frameworks/base/core/java/android/app/LoadedApk.java文件中:

这里mActivityThread成员变量的类型为Handler,它是前面MainActivity注册广播接收器时,从ActivityThread取得的,具体可以参考前面一篇文章Android应用程序注册广播接收器(registerReceiver)的过程分析中的Step 3。这里ReceiverDispatcher借助这个Handler,把这个广播以消息的形式放到MainActivity所在的这个ActivityThread的消息队列中去,因此,ReceiverDispatcher不等这个广播被MainActivity处理就返回了,这里也体现了广播的发送和处理是异步进行的。

注意这里处理消息的方式是通过Handler.post函数进行的,post函数的参数是Runnable类型的,这个消息最终会调用这个这个参数的run成员函数来处理。这里的Args类是LoadedApk类的内部类ReceiverDispatcher的一个内部类,它继承于Runnable类,因此,可以作为mActivityThread.post的参数传进去,代表这个广播的intent也保存在这个Args实例中。

Step 15. Hanlder.post

这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中,这个函数我们就不看了,有兴趣的读者可以自己研究一下,它的作用就是把消息放在消息队列中,然后就返回了,这个消息最终会在传进来的Runnable类型的参数的run成员函数中进行处理。

Step 16. Args.run

这个函数定义在frameworks/base/core/java/android/app/LoadedApk.java文件中:

这里的mReceiver是ReceiverDispatcher类的成员变量,它的类型是BroadcastReceiver,这里它就是MainActivity注册广播接收器时创建的BroadcastReceiver实例了,具体可以参考前面一篇文章Android应用程序注册广播接收器(registerReceiver)的过程分析中的Step 2。

有了这个ReceiverDispatcher实例之后,就可以调用它的onReceive函数把这个广播分发给它处理了。

Step 17. BroadcastReceiver.onReceive

这个函数定义Android系统中的广播(Broadcast)机制简要介绍和学习计划一文中所介绍的Android应用程序Broadcast的工程目录下的src/shy/luo/broadcast/MainActivity.java文件中:

这样,MainActivity里面的定义的BroadcastReceiver实例counterActionReceiver就收到这个广播并进行处理了。
至此,Android应用程序发送广播的过程就分析完成了,结合前面这篇分析广播接收器注册过程的文章Android应用程序注册广播接收器(registerReceiver)的过程分析,就会对Android系统的广播机制且个更深刻的认识和理解了。

最后,我们总结一下这个Android应用程序发送广播的过程:

1. Step 1 - Step 7,计数器服务CounterService通过sendBroadcast把一个广播通过Binder进程间通信机制发送给ActivityManagerService,ActivityManagerService根据这个广播的Action类型找到相应的广播接收器,然后把这个广播放进自己的消息队列中去,就完成第一阶段对这个广播的异步分发了;

2. Step 8 - Step 15,ActivityManagerService在消息循环中处理这个广播,并通过Binder进程间通信机制把这个广播分发给注册的广播接收分发器ReceiverDispatcher,ReceiverDispatcher把这个广播放进MainActivity所在的线程的消息队列中去,就完成第二阶段对这个广播的异步分发了;

3. Step 16 - Step 17,ReceiverDispatcher的内部类Args在MainActivity所在的线程消息循环中处理这个广播,最终是将这个广播分发给所注册的BroadcastReceiver实例的onReceive函数进行处理。

这样,Android系统广播机制就学习完成了,希望对读者有所帮助。重新学习Android系统的广播机制,请回到Android系统中的广播(Broadcast)机制简要介绍和学习计划一文中。

分享到:
评论

相关推荐

    Android应用程序发送广播(sendBroadcast)的过程分析.doc

    Android应用程序发送广播(sendBroadcast)的过程分析

    Android Intent发送广播消息实例详解

    Intent的另一种用途是发送广播消息,应用程序和Android系统都可以使用Intent发送广播消息,广播消息的内容是可以与应用程序密切相关的数据信息,也可以是Android的系统信息,例如网络连接变化、电池电量变化、接收的...

    Android实验六.doc

    【实验要求】 1、 练习使用静态方法和动态方法注册广播接收器 2、 练习发送广播消息的方法 3、 完成实验报告 二、实验内容 1、 新建 Android 应用程序项目 BroadcastTest; 2、 业务逻辑代码与界面布局文件如下: 1)...

    Android中的广播(BroadCast)详细介绍

    在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。我们拿广播电台来做个比方。我们平常使用收音机收音是这样的:许许多多不同的广播电台通过特定的频率来发送他们的内容,而我们用户只需要将...

    Google Android SDK开发范例大全(PDF高清完整版1)(4-1)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全(PDF完整版4)(4-4)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全(PDF高清完整版3)(4-3)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全的目录

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Android 创建app快捷方式到主界面.rar

    在android手机中创建应用APP的快捷方式,在本实例中,创建快捷方式是从用户单击了按钮开始,当然这只是演示,在实际使用中,可能由APP安装程序直接创建快捷方式,实现方法与以下代码相似:  //按下了按钮时  ...

    Google Android SDK开发范例大全(完整版附部分源码).pdf

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable...

    Google Android SDK 开发范例大全01

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK 开发范例大全02

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google+Android+SDK开发范例大全

    ) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义...

    Google Android sdk 开发范例大全 部分章节代码

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全(完整版)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Android Intent和Intent Filter详解

     三种应用程序基本组件——activity, service和broadcast receiver——是使用称为intent的消息来激活的。Intent消息传递是一种组件间运行时绑定的机制. intent是Intent对象, 它包含了需要做的操作的描述, 或者, ...

Global site tag (gtag.js) - Google Analytics