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等发送给AMS
           final 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传进来的是true
           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<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<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null,每一个广播接收者对应一个ReceiverDispatcher(广播分发者),并且把最后把这个map以Context为key存在mReceivers中,而这个Context跟广播的发送方有关系,如果是在Activity中发送的,这个Context就指向与这个Activity,如果是在Service中发送的,这个Context就指向了这个Service。那么这个广播分发者有什么用呢?用map来存储,表示ReceiverDispatcher跟BroadcastReceiver是一一对应的,每个广播接收者对应一个广播分发者, 当AMS向app发送广播时会调用到app进程的广播分发者,然后再将广播以message形式post到app的主线程,来执行onReceive()方法。

假设我的app只有两个Activity(HomeActivity和DetailActivity),这个app被打包成xxx.apk,那么在内存中,这个xxx.apk由LoadedApk来描述,如果HomeActivity和DetailActivity都注册了广播,那么LoadedApk内部维持的mReceivers的长度就为2。现在梳理一下上面的代码,当一个BroadcastReceiver要注册的时候,会优先使用Context查看一下,这个组件有没有注册过广播,如果有就取出来,类型是一个ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> 的map。如果没有,就把map创建出来,并且存到mReceivers中。有了map之后,需要把ReceiverDispatcher(广播分发者)存到map里面去。第一次注册时候,肯定走的是if(rd==null)的代码块里面,这样rd对象被创建出来。现在分析一下ReceiverDispatcher这个类。

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中的action
           Iterator<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();
               }
           }

         //返回值是一个Intent
           return sticky;
       }
   }

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

在Android系统中,系统每加载一个apk,就会有一个LoadedApk对象。而每个LoadedApk对象里会有一张名字为mReceivers的HashMap,用来记录每个apk里面动态注册了那些广播接收者。mReceivers的类型是ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>,泛型中的Context代表是谁注册的,后面的值也是个map ,ArrayMap<BroadcastReceiver, ReceiverDispatcher>表示BroadcastReceiver与ReceiverDispatcher一一对应,一个BroadcastReceiver对应一个ReceiverDispatcher,通过这个定义可以知道,不管一个Activity注册了多少BroadcastReceiver,ReceiverDispatcher只有一个。ReceiverDispatcher内部有一个InnerReceiver的Binder对象,最终是把这个InnerReceiver发送给了AMS,AMS内部也维护了一张表mRegisteredReceivers,用来记录所有动态注册的接收者,首先会根据传进来的InnerReceiver对象取出来一个ReceiverList,ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()),所以实质上每个ReceiverList都对应着Client端的一个ReceiverDispatcher。最后把创建的对象BroadcastFilter(AMS内部广播接收者用BroadcastFilter来表示)加到ReceiverList接收者队列中,注册完毕!

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();
@Override
   public @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;
       }

       // reader
       synchronized (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,这段逻辑想写清楚篇幅会比较大,所以,不深入讨论,以上关于广播的动态注册和静态注册就介绍完了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容