Android源码解析四大组件系列(五)---广播的注册过程

2024-09-02 14:58

本文主要是介绍Android源码解析四大组件系列(五)---广播的注册过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

广播这个篇幅打算用四篇文章来写,分别为广播注册、广播处理、广播的发送,广播深入细节理解,如果都写到一篇文章会比较长,所以拆分成四篇来写。

第一篇
Android源码解析—广播的注册过程
第二篇
Android源码解析—广播的处理过程
第三篇
Android源码解析—广播的发送过程
第四篇
Android源码解析—广播深入细节理解

想收到广播(Broadcast),必须先要注册接收广播的组件—广播接收者(receiver),广播接收者的注册分为动态注册和静态注册,而注册中心就是AMS,AMS再把广播分发到各个广播接收者(receiver)。

image.png

一个广播可以有多个receiver来接收它,注册的方式分为两种,一种是静态注册,一种是动态注册,动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期,在Activity结束前,需要移除广播接收器。 静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

1.1 动态广播注册

动态注册是由ContextImpl的registerReceiver方法调用registerReceiverInternal来注册的

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {IIntentReceiver rd = null;if (receiver != null) {if (mPackageInfo != null && context != null) {//为空表示默认为主线程if (scheduler == null) {//AMS并不是直接给广播接收者发送广播的,当广播到达应用程序进程的时候,会被封装成一个Message,然后push到主线程消息队列中,然后才会给接收者处理,你也可以指定一个处理的Handler,将onReceive()调度在非主线程执行。scheduler = mMainThread.getHandler();}rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);} else {if (scheduler == null) {scheduler = mMainThread.getHandler();}rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver();}}try {//将rd,filter等发送给AMSfinal Intent intent = ActivityManagerNative.getDefault().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName,rd, filter, broadcastPermission, userId);if (intent != null) {intent.setExtrasClassLoader(getClassLoader());intent.prepareToEnterProcess();}return intent;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

传进来的receiver不是直接发送给AMS的,首先会把receiver封装成一个IIntentReceiver对象rd,这个rd是一个binder本地对象,具备了跨进程通信的能力。mPackageInfo是LoadedApk,LoadedApk这个类包含了当前加载的apk的主要的信息,其中成员变量mReceivers表就记录了所有动态注册的receiver。

 private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

rd的获取有两种,当mPackageInfo存在时候,就通过mPackageInfo.getReceiverDispatcher()来获取。

    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd = null;ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;//registered传进来的是trueif (registered) {map = mReceivers.get(context);if (map != null) {rd = map.get(r);}}if (rd == null) {rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);if (registered) {if (map == null) {map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();mReceivers.put(context, map);}map.put(r, rd);}} else {//检查广播分发者的context、handler是否一致rd.validate(context, handler);}rd.mForgotten = false;return rd.getIIntentReceiver();}}

这个方法内部维护了一张表 ArrayMap

 static final class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;final LoadedApk.ReceiverDispatcher mStrongRef;InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);mStrongRef = strong ? rd : null;}.......final IIntentReceiver.Stub mIIntentReceiver;final BroadcastReceiver mReceiver;final Context mContext;final Handler mActivityThread;.......ReceiverDispatcher(BroadcastReceiver receiver, Context context,Handler activityThread, Instrumentation instrumentation,boolean registered) {if (activityThread == null) {throw new NullPointerException("Handler must not be null");}mIIntentReceiver = new InnerReceiver(this, !registered);//广播接收者mReceiver = receiver;//表示哪个发送的广播mContext = context;//主线程mActivityThread = activityThread;.......}.......IIntentReceiver getIIntentReceiver() {return mIIntentReceiver;}.......}

在内部会创建InnerReceiver,InnerReceiver是ReceiverDispatcher的内部类,是一个实现Binder的本地对象,前面也说过了,最终是将一个InnerReceiver对象注册到了AMS中。

OK,绕了这么一大圈子,其实就是为了封装一个InnerReceiver用于和AMS通信,我也不知道谷歌这帮程序员怎么想的,有点麻烦。忽略跨进程的代码,现在由用户进程走到SystemServer进程了,即走到AMS的registerReceiver方法。

 public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {enforceNotIsolatedCaller("registerReceiver");ArrayList<Intent> stickyIntents = null;ProcessRecord callerApp = null;int callingUid;int callingPid;synchronized(this) {if (caller != null) {//由caller获取当前进程对象callerApp = getRecordForAppLocked(caller);//进程还没创建,直接抛出异常if (callerApp == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + Binder.getCallingPid()+ ") when registering receiver " + receiver);}if (callerApp.info.uid != Process.SYSTEM_UID &&!callerApp.pkgList.containsKey(callerPackage) &&!"android".equals(callerPackage)) {throw new SecurityException("Given caller package " + callerPackage+ " is not running in process " + callerApp);}callingUid = callerApp.info.uid;callingPid = callerApp.pid;} else {callerPackage = null;callingUid = Binder.getCallingUid();callingPid = Binder.getCallingPid();}userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,ALLOW_FULL_ONLY, "registerReceiver", callerPackage);//获取IntentFilter中的actionIterator<String> actions = filter.actionsIterator();if (actions == null) {ArrayList<String> noAction = new ArrayList<String>(1);noAction.add(null);actions = noAction.iterator();}//从actions中,先把粘性广播帅选出来,放进stickyIntents中int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };while (actions.hasNext()) {String action = actions.next();for (int id : userIds) {//从mStickyBroadcasts中查看用户的sticky Intent,mStickyBroadcasts存了系统所有的粘性广播ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);if (stickies != null) {ArrayList<Intent> intents = stickies.get(action);if (intents != null) {if (stickyIntents == null) {stickyIntents = new ArrayList<Intent>();}stickyIntents.addAll(intents);}}}}}ArrayList<Intent> allSticky = null;if (stickyIntents != null) {final ContentResolver resolver = mContext.getContentResolver();// Look for any matching sticky broadcasts...for (int i = 0, N = stickyIntents.size(); i < N; i++) {Intent intent = stickyIntents.get(i);// If intent has scheme "content", it will need to acccess// provider that needs to lock mProviderMap in ActivityThread// and also it may need to wait application response, so we// cannot lock ActivityManagerService here.if (filter.match(resolver, intent, true, TAG) >= 0) {if (allSticky == null) {allSticky = new ArrayList<Intent>();}allSticky.add(intent);}}}// The first sticky in the list is returned directly back to the client.Intent sticky = allSticky != null ? allSticky.get(0) : null;if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);//如果receiver为空,就直接返回了if (receiver == null) {return sticky;}synchronized (this) {if (callerApp != null && (callerApp.thread == null|| callerApp.thread.asBinder() != caller.asBinder())) {// 进程不存在(死亡了),也是不能注册成功的return null;}//mRegisteredReceivers表存了所有动态注册的广播接收者,由receiver作为key,获取到ReceiverList,为什么是ReceiverList,而不是一个Receiver呢,因为一个广播可能会有多个接收者,最好整成一个队列或者链表的形式,而ReceiverList继承ArrayList,满足这个需求。每个ReceiverList都对应着Client端的一个ReceiverDispatcher。ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());if (rl == null) {rl = new ReceiverList(this, callerApp, callingPid, callingUid,userId, receiver);if (rl.app != null) {//把广播接收者列表加到这个进程对象的receivers中rl.app.receivers.add(rl);} else {try {//进程不存在,注册死亡通知receiver.asBinder().linkToDeath(rl, 0);} catch (RemoteException e) {return sticky;}rl.linkedToDeath = true;}//新创建的接收者队列,添加到已注册广播队列。mRegisteredReceivers.put(receiver.asBinder(), rl);} else if (rl.uid != callingUid) {throw new IllegalArgumentException("Receiver requested to register for uid " + callingUid+ " was previously registered for uid " + rl.uid);} else if (rl.pid != callingPid) {throw new IllegalArgumentException("Receiver requested to register for pid " + callingPid+ " was previously registered for pid " + rl.pid);} else if (rl.userId != userId) {throw new IllegalArgumentException("Receiver requested to register for user " + userId+ " was previously registered for user " + rl.userId);}//在AMS内部,广播接收者实际上是BroadcastFilter来描述的,由filter等参数创建BroadcastFilter对象,并添加到接收者队列,注意只有registerReceiver()过程才会创建BroadcastFilter,也就是该对象用于动态注册的广播Receiver;,静态的接收者对象不是BroadcastFilter。BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId);rl.add(bf);if (!bf.debugCheck()) {Slog.w(TAG, "==> For Dynamic broadcast");}mReceiverResolver.addFilter(bf);//如果是粘性广播,创建BroadcastRecord,并添加到BroadcastQueue的并行广播队列(mParallelBroadcasts),注册后调用AMS来尽快处理该广播。if (allSticky != null) {ArrayList receivers = new ArrayList();receivers.add(bf);final int stickyCount = allSticky.size();for (int i = 0; i < stickyCount; i++) {Intent intent = allSticky.get(i);BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, null,null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,null, 0, null, null, false, true, true, -1);queue.enqueueParallelBroadcastLocked(r);queue.scheduleBroadcastsLocked();}}//返回值是一个Intentreturn sticky;}}

总结一下:动态注册是调用registerReceiver来注册的,大致流程如下:

在Android系统中,系统每加载一个apk,就会有一个LoadedApk对象。而每个LoadedApk对象里会有一张名字为mReceivers的HashMap,用来记录每个apk里面动态注册了那些广播接收者。mReceivers的类型是ArrayMap

1.2 静态广播注册

静态注册就是在manifest中注册。

<receiver android:name=".MyReceiver"><intent-filter><action android:name="android.intent.action.MY_BROADCAST"/><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</receiver>

它们的信息会在系统启动时,由PackageManagerService(PMS)解析(在该类的构造方法中会对各个应用安装目录的apk文件进行扫描解析)并记录下来。

 if (tagName.equals("activity")) {Activity a = parseActivity(owner, res, parser, flags, outError, false,owner.baseHardwareAccelerated);if (a == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.activities.add(a);} else if (tagName.equals("receiver")) {Activity a = parseActivity(owner, res, parser, flags, outError, true, false);if (a == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.receivers.add(a);} else if (tagName.equals("service")) {Service s = parseService(owner, res, parser, flags, outError);if (s == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.services.add(s);} else if (tagName.equals("provider")) {Provider p = parseProvider(owner, res, parser, flags, outError);if (p == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.providers.add(p);}

经过上面的解析receiver就被保存到了owner.receivers中去了。然后AM会调用PMS的接口来查询“和intent匹配的组件”时,PMS内部就会去查询当初记录下来的数据,并把结果返回AMS。

  List<ResolveInfo> newReceivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
 @Overridepublic @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,String resolvedType, int flags, int userId) {return new ParceledListSlice<>(queryIntentReceiversInternal(intent, resolvedType, flags, userId));}private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,String resolvedType, int flags, int userId) {if (!sUserManager.exists(userId)) return Collections.emptyList();flags = updateFlagsForResolve(flags, userId, intent);ComponentName comp = intent.getComponent();if (comp == null) {if (intent.getSelector() != null) {intent = intent.getSelector();comp = intent.getComponent();}}if (comp != null) {List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);ActivityInfo ai = getReceiverInfo(comp, flags, userId);if (ai != null) {ResolveInfo ri = new ResolveInfo();ri.activityInfo = ai;list.add(ri);}return list;}// readersynchronized (mPackages) {String pkgName = intent.getPackage();if (pkgName == null) {return mReceivers.queryIntent(intent, resolvedType, flags, userId);}final PackageParser.Package pkg = mPackages.get(pkgName);if (pkg != null) {return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,userId);}return Collections.emptyList();}}

因为涉及PMS,这段逻辑想写清楚篇幅会比较大,所以,不深入讨论,以上关于广播的动态注册和静态注册就介绍完了。

这篇关于Android源码解析四大组件系列(五)---广播的注册过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130278

相关文章

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

Go路由注册方法详解

《Go路由注册方法详解》Go语言中,http.NewServeMux()和http.HandleFunc()是两种不同的路由注册方式,前者创建独立的ServeMux实例,适合模块化和分层路由,灵活性高... 目录Go路由注册方法1. 路由注册的方式2. 路由器的独立性3. 灵活性4. 启动服务器的方式5.

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

PLsql Oracle 下载安装图文过程详解

《PLsqlOracle下载安装图文过程详解》PL/SQLDeveloper是一款用于开发Oracle数据库的集成开发环境,可以通过官网下载安装配置,并通过配置tnsnames.ora文件及环境变... 目录一、PL/SQL Developer 简介二、PL/SQL Developer 安装及配置详解1.下

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper