今天我们聊一个大家耳熟能详,经常用到的东西 ------------ 广播。先来看看官方给出的解释:
Generally speaking, broadcasts can be used as a messaging system across apps and outside of the normal user flow.
广播的作用如其名,可以跨进程、跨应用的传递消息。
我们都知道广播的注册在Android中分两类:静态注册
和动态注册
。那么我们先来研究下广播的注册具体做了什么
静态注册
指的是直接在AndroidManifest.xml
使用receiver
标签配置信息。这里以订阅服务配置接收推送消息的广播为例:
ini
<receiver
android:name="com.lotus.subscription.PushMessageReceiver"
android:exported="true">
<intent-filter>
<action android:name="ecarx.intent.action.PUSH_RECEIVER" />
</intent-filter>
</receiver>
1. Receiver标签的识别流程
既然是直接在AndroidManifest.xml
使用receiver
标签,那么想要找到这个标签的对应信息,肯定需要涉及文件的解析。
1.1 APK文件的安装
APK文件的安装流程也较为复杂,这里就不从头开始分析了,直接从PackageManagerService
中处理安装动作开始进行分析:
- 执行
PackageManagerService#installStage
时,发送一条Message
到Handler
中
2. 在Handler
中处理Message#obj
方法传递过来的InstallParams#startCopy
,InstallParams#startCopy
方法中调用InstallParams#handlerstartCopy
和InstallParams#handlerReturnCode
。
- 在
InstallParams#handlerReturnCode
中调用了InstallParams#processPendingInstall
- 在
InstallParams#processPendingInstall
中根据不同的情况调用InstallParams#tryProcessInstallRequest
和InstallParams#processInstallRequestsAsync
,在最终也会调用到InstallParams#processInstallRequestsAsync
- 会在Handler中将收到的InstallRequest依次执行
doPreInstall(校验返回值状态)
、installPackagesTracedLI(执行installPackagesLI安装包解析APK)
、doPostInstall(同安装前一样校验状态)
、restoreAndPostInstall(备份信息或失败处理以及安装成功的逻辑)
。
1.2 APK文件解析
我们需要关注的是如何解析AndroidManifest.xml
文件中的receiver
标签,那么只需要关注installPackagesLI
方法即可。
- 调用
preparePackageLI
去准备解析包信息
- 接着调用
parsePackage
读取文件信息
在PackageParser2#parsePackage
中调用ParsingPackageUtils#parsePackage
解析包内容
- 此时解析文件,分为
包含文件目录的解析parseClusterPackage
和单个整体包解析parseMonolithicPackage
,最终都会调用parseBaseApk
。
- 后面会调用到
parseBaseApkTags
来解析AndroidManifest.xml
中的相关标签
- 通过前置的校验后,调用
parseBaseApplication
来解析application标签
下的内容。
2. 将<receiver>标签
下的信息解析后进行记录
通过Xml解析,按照对应的标签,找到<receiver>标签
并解析其内容
最后将解析的结果ParseResult
,通过ParsingPackage#addReceiver
存储在ParsingPackageImpl#receivers
。
动态注册
在静态注册的方式中,我们结合源码对具体流程进行了展示。所以在动态注册中,通过流程图来对调用方式进行阐述。
指定onReceiver收到的线程
通常我们使用广播时,在onReceiver
接收消息时,都默认会在主线程。那么是否存在onReceiver
在其他线程调用的情况?
在动态注册时,可以通过传递需要接收消息的线程对应的Handler
来实现此效果:
less
/**
* Register to receive intent broadcasts, to run in the context of
* <var>scheduler</var>. See
* { @link #registerReceiver(BroadcastReceiver, IntentFilter)} for more
* information. This allows you to enforce permissions on who can
* broadcast intents to your receiver, or have the receiver run in
* a different thread than the main application thread.
*
* <p>See { @link BroadcastReceiver} for more information on Intent broadcasts.
*
* <p>As of { @link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
* registered with this method will correctly respect the
* { @link Intent#setPackage(String)} specified for an Intent being broadcast.
* Prior to that, it would be ignored and delivered to all matching registered
* receivers. Be careful if using this for security.</p>
*
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
* @param broadcastPermission String naming a permissions that a
* broadcaster must hold in order to send an Intent to you. If null,
* no permission is required.
* @param scheduler Handler identifying the thread that will receive
* the Intent. If null, the main thread of the process will be used.
*
* @return The first sticky intent found that matches <var>filter</var>,
* or null if there are none.
*
* @see #registerReceiver(BroadcastReceiver, IntentFilter)
* @see #sendBroadcast
* @see #unregisterReceiver
*/
@Nullable
public abstract Intent registerReceiver(BroadcastReceiver receiver,
IntentFilter filter, @Nullable String broadcastPermission,
@Nullable Handler scheduler);
这样其注册时,就会将传递的Handler
信息进行记录,在收到消息时就会通过此Handler
切换到对应线程后执行onReceiver
。