Android Broadcast原理分析之registerReceiver(一)

Android Broadcast原理分析之registerReceiver(一)目录 BroadcastRec 概述 BroadcastRec 分类 registerRece 流程图源码解析总结 1 BroadcastRec 概述广播作为四大组件之一 在平时开发过程中会大量使用到 使用方式也是多种多样的 既可以自己在 manifest 中注册 也可以在 java 代码中动态注册 既可以接收由系统发出的广播 也可以接受自己定义并发送的广播 广播

目录

  • BroadcastReceiver概述
  • BroadcastReceiver分类
  • registerReceiver流程图
  • 源码解析
  • 总结

1. BroadcastReceiver概述

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

2. BroadcastReceiver分类

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

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

 private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) { if (scheduler == null) { // 注册receiver的时候可以指定接受recover的Handler // 如果没有指定,则默认用主线程的handler处理 scheduler = mMainThread.getHandler(); } // 获取IIntentReceiver // 这个是一个Binder对象,当广播来临时,用于AMS向客户端发起回调 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 { // binder call至AMS,进行广播注册 final Intent intent = ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags); if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); intent.prepareToEnterProcess(); } return intent; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } 

4.2 LoadedApk.getReceiverDispatcher

 public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r, Context context, Handler handler, Instrumentation instrumentation, boolean registered) { synchronized (mReceivers) { // 如果Context相同,每个receiver对应一个ReceiverDispatcher LoadedApk.ReceiverDispatcher rd = null; ArrayMap 
  
    map = null; if (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 
   
     (); mReceivers.put(context, map); } map.put(r, rd); } } else { rd.validate(context, handler); } rd.mForgotten = false; return rd.getIIntentReceiver(); } } 
    
  

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




 static final class ReceiverDispatcher { // 是一个binder对象 final static class InnerReceiver extends IIntentReceiver.Stub { final WeakReference 
  
    mDispatcher; final LoadedApk.ReceiverDispatcher mStrongRef; InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) { mDispatcher = new WeakReference 
   
     (rd); mStrongRef = strong ? rd : null; } @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { // 这里就是广播真正派发到本进程的时候从systemserver binder call过来执行的 ... } final IIntentReceiver.Stub mIIntentReceiver; final BroadcastReceiver mReceiver; final Context mContext; final Handler mActivityThread; final Instrumentation mInstrumentation; final boolean mRegistered; final IntentReceiverLeaked mLocation; RuntimeException mUnregisterLocation; boolean mForgotten; ... } 
    
  

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

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

4.3 ActivityManagerService.registerReceiver

 public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { enforceNotIsolatedCaller("registerReceiver"); ArrayList 
  
    stickyIntents = null; ProcessRecord callerApp = null; final boolean visibleToInstantApps = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0; int callingUid; int callingPid; boolean instantApp; synchronized(this) { if (caller != null) { // 正常来讲caller是发起binder call的客户端进程对应的ApplicationThread对象 // 如果为null则抛异常 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 != 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(); } // 判断caller是否为instant app instantApp = isInstantApp(callerApp, callerPackage, callingUid); userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage); // 获取广播注册的filter中的action封装到list中 Iterator 
   
     actions = filter.actionsIterator(); if (actions == null) { ArrayList 
    
      noAction = new ArrayList 
     
       (1); noAction.add(null); actions = noAction.iterator(); } // mStickyBroadcasts是一个二级map // 一级key是userId,二级key是广播对应的action,value是广播对应intent的list(一般只有一个intent) // 这里是为了查询对于当前user,本次注册的所有action对应的sticky广播的intent int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { String action = actions.next(); for (int id : userIds) { ArrayMap 
      
        > stickies = mStickyBroadcasts.get(id); if (stickies != null) { ArrayList 
       
         intents = stickies.get(action); if (intents != null) { if (stickyIntents == null) { stickyIntents = new ArrayList 
        
          (); } stickyIntents.addAll(intents); } } } } } ArrayList 
         
           allSticky = null; // 这里不为null表示本次注册的广播中有sticky广播 if (stickyIntents != null) { final ContentResolver resolver = mContext.getContentResolver(); // 查找匹配的sticky广播 for (int i = 0, N = stickyIntents.size(); i < N; i++) { Intent intent = stickyIntents.get(i); // 如果caller是instant app,且intent的flag不允许对instant可见,则跳过 if (instantApp && (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) { continue; } // 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 
          
            (); } allSticky.add(intent); } } } // 直接把最近的一个匹配到的sticky广播返回 Intent sticky = allSticky != null ? allSticky.get(0) : null; // 广播注册的时候receiver是可以为null的,这种情况下这里直接return if (receiver == null) { return sticky; } synchronized (this) { // 校验caller进程是否正常 if (callerApp != null && (callerApp.thread == null || callerApp.thread.asBinder() != caller.asBinder())) { // Original caller already died return null; } // mRegisteredReceivers中存放了所有的已注册的receiver // 每个BroadcastReceiver对应一个InnerReceiver,即Binder对象 // binder对象做key,value是ReceiverList // ReceiverList是一个ArrayList ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) { rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver); if (rl.app != null) { rl.app.receivers.add(rl); } else { try { // 如果是新创建的receiver,还需要linkToDeath receiver.asBinder().linkToDeath(rl, 0); } catch (RemoteException e) { return sticky; } rl.linkedToDeath = true; } // 放入mRegisteredReceivers 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 + " callerPackage is " + callerPackage); } else if (rl.pid != callingPid) { throw new IllegalArgumentException( "Receiver requested to register for pid " + callingPid + " was previously registered for pid " + rl.pid + " callerPackage is " + callerPackage); } else if (rl.userId != userId) { throw new IllegalArgumentException( "Receiver requested to register for user " + userId + " was previously registered for user " + rl.userId + " callerPackage is " + callerPackage); } // 每一个IntentFilter对应一个BroadcastFilter BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId, instantApp, visibleToInstantApps); // receiverList中存放了通过这个receiver注册的所有的filter // 每调用一次register就会add一次 rl.add(bf); // mReceiverResolver中存放所有的BroadcastFilter mReceiverResolver.addFilter(bf); // 有匹配的sticky广播,则直接开始调度派发 if (allSticky != null) { ArrayList receivers = new ArrayList(); receivers.add(bf); // 对于每一个sticky广播,创建BroadcastRecord并入队(并行) final int stickyCount = allSticky.size(); for (int i = 0; i < stickyCount; i++) { Intent intent = allSticky.get(i); // 根据flag是否有FLAG_RECEIVER_FOREGROUND判断入队是前台还是后台队列 BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers, null, 0, null, null, false, true, true, -1); // 入队,并行队列 queue.enqueueParallelBroadcastLocked(r); // 启动广播的调度,也就是开始派发广播 queue.scheduleBroadcastsLocked(); } } return sticky; } } 
           
          
         
        
       
      
     
    
  

上面主要做了几件事情:

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

5. 总结

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/206462.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午3:38
下一篇 2026年3月19日 下午3:39


相关推荐

  • collection.toArray(new String[0])中new String[0]的作用

    collection.toArray(new String[0])中new String[0]的作用collection.toArray(newString[0])中newString[0]的作用转载2016年02月19日14:35:163372newstring[0]的作用比如:String[]result=set.toArray(newString[0]);Collection的公有方法中,toArray()是比较重要的一个。但是使用无参数的toArr…

    2022年5月14日
    45
  • Linux更改文件名

    Linux更改文件名使用 mvab 就可以重命名了 mv move 移动文件 延伸功能 重命名 linux 系统没有专门的重命名命名 基本格式 mvfilenamene 转载至 https blog csdn net id article details

    2026年3月19日
    2
  • pycharm+anaconda安装教程_anaconda配置pycharm

    pycharm+anaconda安装教程_anaconda配置pycharm对了宝贝儿们,卑微小李的公众号【野指针小李】已开通,期待与你一起探讨学术哟~摸摸大!目录1WhyAnaconda?2Anaconda安装流程2.1卸载python2.2下载anaconda2.2安装anaconda1WhyAnaconda?Anaconda最大的优势我认为在于可以做环境管理,可以通过创建不同的环境,安装不同的包。简单来说,就比如我们在github上下载了一个代码,是用python2.6写的,但是我们的python是3.7的版本。如果我们没有用anaconda,那么我们

    2022年8月27日
    8
  • Kolmogorov–Smirnov test(K-S检验)「建议收藏」

    Kolmogorov–Smirnov test(K-S检验)「建议收藏」主要参考资料:(1)https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test(2)https://wenku.baidu.com/view/ccfa573a3968011ca30091d6.htmlKolmogorov–Smirnovstatistic累计分布函数:其中I[−inf,x]I[−inf…

    2022年10月18日
    3
  • 织梦php调用底部,dede底部出现织梦官方版权链接”Power by DedeCms”

    织梦php调用底部,dede底部出现织梦官方版权链接”Power by DedeCms”显示结果:自从dedecms织梦系统更新到6.7日的版本,底部版权信息调用标签{dede:global.cfg_powerby/}会自动加上织梦官方的链接[PowerbyDedeCms],想必很多新用户使用中都想去除这个官方的链接,由于这是官方的作为,本文只是提供一个技术的交流,并不鼓励大家去除底部的链接。好了,回归正题,大家知道,进入后台在后台的系统-系统参数有个网站版权信息,在这里并不能…

    2022年7月13日
    17
  • 生产环境必读:如何将 n8n 默认 SQLite 数据库迁移至 PostgreSQL?

    生产环境必读:如何将 n8n 默认 SQLite 数据库迁移至 PostgreSQL?

    2026年3月15日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号