经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android Broadcast原理分析之registerReceiver详解
来源:jb51  时间:2021/8/26 10:36:30  对本文有异议

1. BroadcastReceiver概述

广播作为四大组件之一,在平时开发过程中会大量使用到,使用方式也是多种多样的,既可以自己在manifest中注册,也可以在java代码中动态注册,既可以接收由系统发出的广播,也可以接受自己定义并发送的广播。广播可以实现进程内以及跨进程之间的通信。从本文开始将分别介绍广播的注册,广播的派发,本地广播(LocalBroadcast)以及Android O上对广播的限制,本文主要介绍广播动态注册。

2. BroadcastReceiver分类

从注册方式上区分:动态注册以及静态注册(显示广播和隐式广播)
从发送方式上区分:无序广播和有序广播
从处理类型上区分:前台广播和后台广播
从运行方式上区分:普通广播和Sticky广播(已弃用)
从发送者区分:系统广播和自定义广播
此外还有protect broadcast(只允许指定应用可以发送)
sticky广播:系统保存了一部分广播的状态,当你注册的时候,不需要等到下次这个广播发出来,直接把最近上一次发送的这个广播返回给你

以上的这些概念在接下来的介绍中都会逐个涉及。

3. registerReceiver流程图

registerReceiver

其中的APP,ContextImpl,LoadedApk,ActivityManagerProxy都在APP本身的进程中,ActivityManagerService在system_server进程中。

  1. 首先在APP的进程中发起广播的注册,通过registerReceiver接口,这个方法有很多重载方法,但是最终的入口都是在ContextImpl中,后面会详细介绍
  2. 从之前的Context的学习可以知道,registerReceiver最终调用的实现在ContextImpl
  3. 如果没有指定处理广播的handler,则默认使用主线程的handler
  4. 获取要注册的ReceiverDispatcher,在注册的Context相同的情况下,每个Receiver对应一个ReceiverDispatcher
  5. 通过binder call到systemserver进行广播注册

4. 源码解析

4.1 ContextImpl.registerReceiverInternal

  1. private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
  2. IntentFilter filter, String broadcastPermission,
  3. Handler scheduler, Context context, int flags) {
  4. IIntentReceiver rd = null;
  5. if (receiver != null) {
  6. if (mPackageInfo != null && context != null) {
  7. if (scheduler == null) {
  8. // 注册receiver的时候可以指定接受recover的Handler
  9. // 如果没有指定,则默认用主线程的handler处理
  10. scheduler = mMainThread.getHandler();
  11. }
  12. // 获取IIntentReceiver
  13. // 这个是一个Binder对象,当广播来临时,用于AMS向客户端发起回调
  14. rd = mPackageInfo.getReceiverDispatcher(
  15. receiver, context, scheduler,
  16. mMainThread.getInstrumentation(), true);
  17. } else {
  18. if (scheduler == null) {
  19. scheduler = mMainThread.getHandler();
  20. }
  21. rd = new LoadedApk.ReceiverDispatcher(
  22. receiver, context, scheduler, null, true).getIIntentReceiver();
  23. }
  24. }
  25. try {
  26. // binder call至AMS,进行广播注册
  27. final Intent intent = ActivityManager.getService().registerReceiver(
  28. mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
  29. broadcastPermission, userId, flags);
  30. if (intent != null) {
  31. intent.setExtrasClassLoader(getClassLoader());
  32. intent.prepareToEnterProcess();
  33. }
  34. return intent;
  35. } catch (RemoteException e) {
  36. throw e.rethrowFromSystemServer();
  37. }
  38. }

参数解析:

receiver:将要注册的receiver
userId:用户空间标志,默认情况下我们都只有一个user,现在一些手机推出的分身,其实就是用的第二个user,这种情况下userid会变,否则默认主空间的都是0
IntentFilter:要注册的广播的filter
broadcastPermission:指定要注册的广播的权限
scheduler:指定广播接受(也就是onReceive)所在的线程,也就是说注册的时候就可以指定好广播处理放在哪个线程,如果receiver中事情太多,可以放在另外一个线程,这样可以避免主线程被卡住
context:通过getOuterContext获取到,前面在介绍context的时候有提到,application/service/activity中获取到的是不一样的
flags:注册广播所携带的flag

4.2 LoadedApk.getReceiverDispatcher

  1. public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
  2. Context context, Handler handler,
  3. Instrumentation instrumentation, boolean registered) {
  4. synchronized (mReceivers) {
  5. // 如果Context相同,每个receiver对应一个ReceiverDispatcher
  6. LoadedApk.ReceiverDispatcher rd = null;
  7. ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
  8. if (registered) {
  9. map = mReceivers.get(context);
  10. if (map != null) {
  11. rd = map.get(r);
  12. }
  13. }
  14. if (rd == null) {
  15. rd = new ReceiverDispatcher(r, context, handler,
  16. instrumentation, registered);
  17. if (registered) {
  18. if (map == null) {
  19. map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
  20. mReceivers.put(context, map);
  21. }
  22. map.put(r, rd);
  23. }
  24. } else {
  25. rd.validate(context, handler);
  26. }
  27. rd.mForgotten = false;
  28. return rd.getIIntentReceiver();
  29. }
  30. }

mReceivers是一个二级map,一级key是context,二级key是BroadcastReceiver,value是ReceiverDispatcher。

这里的ReceiverDispatcher又是什么呢?

它是LoadedApk中的一个内部类,保存了这个receiver的信息,用于在广播派发到本进程的时候执行,上面这方法最重要的是getIIntentReceiver,这个就非常重要了,它是一个Binder对象,说在广播注册的时候将这个binder对象传递到了AMS,然后当广播派发到本进程的时候,通过这个binder对象再会调回来,它在ReceiverDispatcher创建的时候创建。

  1. static final class ReceiverDispatcher {
  2. // 是一个binder对象
  3. final static class InnerReceiver extends IIntentReceiver.Stub {
  4. final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
  5. final LoadedApk.ReceiverDispatcher mStrongRef;
  6.  
  7. InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
  8. mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
  9. mStrongRef = strong ? rd : null;
  10. }
  11.  
  12. @Override
  13. public void performReceive(Intent intent, int resultCode, String data,
  14. Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
  15. // 这里就是广播真正派发到本进程的时候从systemserver binder call过来执行的
  16. ...
  17. }
  18.  
  19. final IIntentReceiver.Stub mIIntentReceiver;
  20. final BroadcastReceiver mReceiver;
  21. final Context mContext;
  22. final Handler mActivityThread;
  23. final Instrumentation mInstrumentation;
  24. final boolean mRegistered;
  25. final IntentReceiverLeaked mLocation;
  26. RuntimeException mUnregisterLocation;
  27. boolean mForgotten;
  28. ...
  29. }

到这里,广播注册在APP进程的流程就走完了,主要做了几件事:

  1. 获取handler
  2. 获取ReceiverDispatcher
  3. 获取InnerReceiver
  4. 将上面这些连带receiver的相关信息,发起binder call到ActivityManagerService

4.3 ActivityManagerService.registerReceiver

  1. public Intent registerReceiver(IApplicationThread caller, String callerPackage,
  2. IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
  3. int flags) {
  4. enforceNotIsolatedCaller("registerReceiver");
  5. ArrayList<Intent> stickyIntents = null;
  6. ProcessRecord callerApp = null;
  7. final boolean visibleToInstantApps
  8. = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
  9. int callingUid;
  10. int callingPid;
  11. boolean instantApp;
  12. synchronized(this) {
  13. if (caller != null) {
  14. // 正常来讲caller是发起binder call的客户端进程对应的ApplicationThread对象
  15. // 如果为null则抛异常
  16. callerApp = getRecordForAppLocked(caller);
  17. if (callerApp == null) {
  18. throw new SecurityException(
  19. "Unable to find app for caller " + caller
  20. + " (pid=" + Binder.getCallingPid()
  21. + ") when registering receiver " + receiver);
  22. }
  23. if (callerApp.info.uid != SYSTEM_UID &&
  24. !callerApp.pkgList.containsKey(callerPackage) &&
  25. !"android".equals(callerPackage)) {
  26. throw new SecurityException("Given caller package " + callerPackage
  27. + " is not running in process " + callerApp);
  28. }
  29. callingUid = callerApp.info.uid;
  30. callingPid = callerApp.pid;
  31. } else {
  32. callerPackage = null;
  33. callingUid = Binder.getCallingUid();
  34. callingPid = Binder.getCallingPid();
  35. }
  36.  
  37. // 判断caller是否为instant app
  38. instantApp = isInstantApp(callerApp, callerPackage, callingUid);
  39. userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
  40. ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
  41.  
  42. // 获取广播注册的filter中的action封装到list中
  43. Iterator<String> actions = filter.actionsIterator();
  44. if (actions == null) {
  45. ArrayList<String> noAction = new ArrayList<String>(1);
  46. noAction.add(null);
  47. actions = noAction.iterator();
  48. }
  49.  
  50. // mStickyBroadcasts是一个二级map
  51. // 一级key是userId,二级key是广播对应的action,value是广播对应intent的list(一般只有一个intent)
  52. // 这里是为了查询对于当前user,本次注册的所有action对应的sticky广播的intent
  53. int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
  54. while (actions.hasNext()) {
  55. String action = actions.next();
  56. for (int id : userIds) {
  57. ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
  58. if (stickies != null) {
  59. ArrayList<Intent> intents = stickies.get(action);
  60. if (intents != null) {
  61. if (stickyIntents == null) {
  62. stickyIntents = new ArrayList<Intent>();
  63. }
  64. stickyIntents.addAll(intents);
  65. }
  66. }
  67. }
  68. }
  69. }
  70.  
  71. ArrayList<Intent> allSticky = null;
  72. // 这里不为null表示本次注册的广播中有sticky广播
  73. if (stickyIntents != null) {
  74. final ContentResolver resolver = mContext.getContentResolver();
  75. // 查找匹配的sticky广播
  76. for (int i = 0, N = stickyIntents.size(); i < N; i++) {
  77. Intent intent = stickyIntents.get(i);
  78. // 如果caller是instant app,且intent的flag不允许对instant可见,则跳过
  79. if (instantApp &&
  80. (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
  81. continue;
  82. }
  83. // If intent has scheme "content", it will need to acccess
  84. // provider that needs to lock mProviderMap in ActivityThread
  85. // and also it may need to wait application response, so we
  86. // cannot lock ActivityManagerService here.
  87. if (filter.match(resolver, intent, true, TAG) >= 0) {
  88. if (allSticky == null) {
  89. allSticky = new ArrayList<Intent>();
  90. }
  91. allSticky.add(intent);
  92. }
  93. }
  94. }
  95.  
  96. // 直接把最近的一个匹配到的sticky广播返回
  97. Intent sticky = allSticky != null ? allSticky.get(0) : null;
  98. // 广播注册的时候receiver是可以为null的,这种情况下这里直接return
  99. if (receiver == null) {
  100. return sticky;
  101. }
  102.  
  103. synchronized (this) {
  104. // 校验caller进程是否正常
  105. if (callerApp != null && (callerApp.thread == null
  106. || callerApp.thread.asBinder() != caller.asBinder())) {
  107. // Original caller already died
  108. return null;
  109. }
  110. // mRegisteredReceivers中存放了所有的已注册的receiver
  111. // 每个BroadcastReceiver对应一个InnerReceiver,即Binder对象
  112. // binder对象做key,value是ReceiverList
  113. // ReceiverList是一个ArrayList
  114. ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
  115. if (rl == null) {
  116. rl = new ReceiverList(this, callerApp, callingPid, callingUid,
  117. userId, receiver);
  118. if (rl.app != null) {
  119. rl.app.receivers.add(rl);
  120. } else {
  121. try {
  122. // 如果是新创建的receiver,还需要linkToDeath
  123. receiver.asBinder().linkToDeath(rl, 0);
  124. } catch (RemoteException e) {
  125. return sticky;
  126. }
  127. rl.linkedToDeath = true;
  128. }
  129. // 放入mRegisteredReceivers
  130. mRegisteredReceivers.put(receiver.asBinder(), rl);
  131. } else if (rl.uid != callingUid) {
  132. throw new IllegalArgumentException(
  133. "Receiver requested to register for uid " + callingUid
  134. + " was previously registered for uid " + rl.uid
  135. + " callerPackage is " + callerPackage);
  136. } else if (rl.pid != callingPid) {
  137. throw new IllegalArgumentException(
  138. "Receiver requested to register for pid " + callingPid
  139. + " was previously registered for pid " + rl.pid
  140. + " callerPackage is " + callerPackage);
  141. } else if (rl.userId != userId) {
  142. throw new IllegalArgumentException(
  143. "Receiver requested to register for user " + userId
  144. + " was previously registered for user " + rl.userId
  145. + " callerPackage is " + callerPackage);
  146. }
  147. // 每一个IntentFilter对应一个BroadcastFilter
  148. BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
  149. permission, callingUid, userId, instantApp, visibleToInstantApps);
  150. // receiverList中存放了通过这个receiver注册的所有的filter
  151. // 每调用一次register就会add一次
  152. rl.add(bf);
  153. // mReceiverResolver中存放所有的BroadcastFilter
  154. mReceiverResolver.addFilter(bf);
  155.  
  156. // 有匹配的sticky广播,则直接开始调度派发
  157. if (allSticky != null) {
  158. ArrayList receivers = new ArrayList();
  159. receivers.add(bf);
  160.  
  161. // 对于每一个sticky广播,创建BroadcastRecord并入队(并行)
  162. final int stickyCount = allSticky.size();
  163. for (int i = 0; i < stickyCount; i++) {
  164. Intent intent = allSticky.get(i);
  165. // 根据flag是否有FLAG_RECEIVER_FOREGROUND判断入队是前台还是后台队列
  166. BroadcastQueue queue = broadcastQueueForIntent(intent);
  167. BroadcastRecord r = new BroadcastRecord(queue, intent, null,
  168. null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
  169. null, 0, null, null, false, true, true, -1);
  170. // 入队,并行队列
  171. queue.enqueueParallelBroadcastLocked(r);
  172. // 启动广播的调度,也就是开始派发广播
  173. queue.scheduleBroadcastsLocked();
  174. }
  175. }
  176. return sticky;
  177. }
  178. }

上面主要做了几件事情:

  1. 对caller的判断
  2. 遍历action,查询是否有匹配的sticky广播
  3. 将本次注册的广播放到mRegisteredReceivers中记录
  4. 如果是sticky广播,开始派发

5. 总结

本文从App的一次广播注册发起开始,到systemserver的注册流程,整体上流程还是比较简单的,顾名思义,注册,正是把要注册的广播在systemserver中进行登记,等到这个事件真正来临的时候,从登记中取出需要被通知的receiver,这也就是后面广播的派发了。
从设计模式的角度讲,这正是经典的观察者模式。

到此这篇关于Android Broadcast原理分析之registerReceiver详解的文章就介绍到这了,更多相关Android Broadcast原理内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号