前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android深入四大组件(四)广播的注册、发送和接收过程

Android深入四大组件(四)广播的注册、发送和接收过程

作者头像
用户1269200
发布2018-02-01 14:38:15
7340
发布2018-02-01 14:38:15
举报
文章被收录于专栏:刘望舒刘望舒

前言

我们接着来学习Android四大组件中的BroadcastReceiver,广播主要就是分为注册、接收和发送过程。建议阅读此文前请先阅读Android深入理解四大组件系列的文章,知识重复的部分,本文不再赘述。

1.广播的注册过程

BroadcastReceiver的注册分为两种,分别是静态注册和动态注册,静态注册在应用安装时由PackageManagerService来完成注册过程,关于这一过程,我会在后续的介绍PackageManagerService文章中详细介绍。这里只介绍BroadcastReceiver的动态注册。 要想动态注册BroadcastReceiver,需要调用registerReceiver方法,它的实现在ContextWrapper中,代码如下所示。

frameworks/base/core/java/android/content/ContextWrapper.java

这里mBase具体指向就是ContextImpl,不明白的请查看Android深入四大组件(二)Service的启动过程这篇文章。ContextImpl的registerReceiver方法有很多重载的方法最终会调用registerReceiverInternal方法:

frameworks/base/core/java/android/app/ContextImpl.java

在注释1处判断如果LoadedApk类型的mPackageInfo不等于null并且context不等null就调用注释2处的代码通过mPackageInfo的getReceiverDispatcher方法获取rd对象,否则就调用注释3处的代码来创建rd对象。注释2和3的代码的目的都是要获取IIntentReceiver类型的rd对象,IIntentReceiver是一个Binder接口,用于进行跨进程的通信,它的具体实现在 LoadedApk.ReceiverDispatcher.InnerReceiver,如下所示。

frameworks/base/core/java/android/app/LoadedApk.java

回到registerReceiverInternal方法,在注释4处调用了ActivityManagerProxy(AMP)的registerReceiver方法,最终会调用AMS的registerReceiver方法,并将rd传就去。不明白的同学请查看Android深入四大组件(一)应用程序启动过程,这里不再赘述。查看AMS的registerReceiver方法,如下所示。

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

注释1处根据传入的IntentFilter类型的filter的得到actions列表,根据actions列表和userIds(userIds可以理解为应用程序的uid)得到所有的粘性广播的intent,并在注释2处传入到stickyIntents中,在注释3处将这些粘性广播的intent存入到allSticky列表中,从这里可以看出粘性广播是存储在AMS中的。 接着查看AMS的registerReceiver方法的剩余内容: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

注释1处获取ReceiverList列表,如果为空则在注释2处创建,ReceiverList继承自ArrayList,用来存储广播接收者。在注释3处创建BroadcastFilter并传入此前创建的ReceiverList,BroadcastFilter用来描述注册的广播接收者,并在注释4通过add方法将自身添加到ReceiverList中。注释5处将BroadcastFilter添加到mReceiverResolver中,这样当AMS接收到广播时就可以从mReceiverResolver中找到对应的广播接收者了。下面给出广播的注册过程的时序图。

2.广播的发送和接收过程

ContextImpl到AMS的调用过程

广播可以发送多种类型,包括无序广播(普通广播)、有序广播和粘性广播,这里以无序广播为例,来讲解广播的发送过程。 要发送无序广播需要调用sendBroadcast方法,它的实现同样在ContextWrapper中: frameworks/base/core/java/android/content/ContextWrapper.java

接着来看ContextImpl中的sendBroadcast方法,如下所示。

frameworks/base/core/java/android/app/ContextImpl.java

注释1处又是熟悉的代码,最终会调用AMS的broadcastIntent方法: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

我们来查看注释1处的verifyBroadcastLocked方法:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

verifyBroadcastLocked方法主要是验证广播是否合法,在注释1处验证intent是否不为null并且有文件描述符。注释2处获得intent中的flag。注释3处如果系统正在启动过程中,判断如果flag设置为FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT(启动检查时只接受动态注册的广播接收者)则不做处理,如果不是则在注释4处判断如果flag没有设置为FLAG_RECEIVER_REGISTERED_ONLY(只接受动态注册的广播接收者)则会抛出异常。我们再回到broadcastIntent方法,在注释2处调用了broadcastIntentLocked方法,代码如下所示。

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

这里省略了很多代码,前面的工作主要是将动态注册的广播接收者和静态注册的广播接收者按照优先级高低存储在不同的列表中,再将这两个列表合并到receivers列表中,这样receivers列表包含了所有的广播接收者(无序广播和有序广播)。在注释1处创建BroadcastRecord对象并将receivers传进去,在注释2处调用BroadcastQueue的scheduleBroadcastsLocked方法。这里先给出ContextImpl到AMS的调用过程的时序图。

AMS到BroadcastReceiver的调用过程

BroadcastQueue的scheduleBroadcastsLocked方法的代码如下所示。 frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

在注释1处向BroadcastHandler类型的mHandler对象发送了BROADCAST_INTENT_MSG类型的消息,这个消息在BroadcastHandler的handleMessage方法中进行处理,如下所示。

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

在handleMessage方法中调用了processNextBroadcast方法,processNextBroadcast方法对无序广播和有序广播分别进行处理,旨在将广播发送给广播接收者,下面给出processNextBroadcast方法中对无序广播的处理部分。 frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

从前面的代码我们得知fromMsg的值为true,因此注释1处会将mBroadcastsScheduled 设置为flase,表示对于此前发来的BROADCAST_INTENT_MSG类型的消息已经处理了。注释2处的mParallelBroadcasts列表用来存储无序广播,通过while循环将mParallelBroadcasts列表中的无序广播发送给对应的广播接收者。在注释3处获取每一个mParallelBroadcasts列表中存储的BroadcastRecord类型的r对象。注释4处将这些r对象描述的广播发送给对应的广播接收者,deliverToRegisteredReceiverLocked方法如下所示。 frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

这里省去了大部分的代码,这些代码是用来检查广播发送者和广播接收者的权限。如果通过了权限的检查,则会调用注释1处的performReceiveLocked方法: frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

注释1和2处的代码表示如果广播接收者所在的应用程序进程存在并且正在运行,则执行注释3处的代码,表示用广播接收者所在的应用程序进程来接收广播,这里app.thread指的是ApplicationThread,我们来查看ApplicationThread的scheduleRegisteredReceiver方法,代码如下所示。 frameworks/base/core/java/android/app/ActivityThread.java

注释1处调用了IIntentReceiver类型的对象receiver的performReceive方法,这里实现receiver的类为LoadedApk.ReceiverDispatcher.InnerReceiver,代码如下所示。 frameworks/base/core/java/android/app/LoadedApk.java

在注释1处调用了ReceiverDispatcher类型的rd对象的performReceive方法:

frameworks/base/core/java/android/app/LoadedApk.java

在注释1处将广播的intent等信息封装为Args对象,并在注释2处调用mActivityThread的post方法并传入了Args对象。这个mActivityThread是一个Handler对象,具体指向的就是H,注释2处的代码就是将Args对象通过H发送到主线程的消息队列中。Args继承自Runnable,这个消息最终会在Args的run方法执行,Args的run方法如下所示。

frameworks/base/core/java/android/app/LoadedApk.java

在注释1处执行了广播接收者的onReceive方法,这样注册的广播接收者就收到了广播并得到了intent。广播的注册、发送和接收过程就讲到这,最后给出剩余部分的调用时序图。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 刘望舒 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1.广播的注册过程
  • 2.广播的发送和接收过程
    • ContextImpl到AMS的调用过程
      • AMS到BroadcastReceiver的调用过程
      相关产品与服务
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档