重温广播注册(详细版)

从ContextImpl开始
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(),
            filter, broadcastPermission, scheduler, getOuterContext());
}

这里从参数中可以看到两个特殊参数,一个时权限,一个是Handler,于此同时传递进去的还有getOuterContext()

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) {
                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 {
        return ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission, userId);
    } catch (RemoteException e) {
        return null;
    }
}

有个参数是通过ActivityThread得到的,这个对象是一个监控对象,在ActivityThread创建的时候创建

public Instrumentation getInstrumentation()
{
    return mInstrumentation;
}
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;
        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 {
            rd.validate(context, handler);
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

在这里需要注意几个数据结构

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

这个方法的逻辑是:

首先会从这个mReceivers数据结构中根据Context获取

ArrayMap<BroadcastReceiver, ReceiverDispatcher>

这个数据结构,如果这个广播有ReceiverDispatcher,则调用

LoadedApk.ReceiverDispatcher.getIIntentReceiver();

将结果返回,如果没有的话将传递进来的

BroadcastReceiver r
Context context
Handler handler
Instrumentation instrumentation

封装成一个ReceiverDispatcher然后添加到mReceivers.put(context, map);

总体来说,就是mReceivers这个数据结构是根据Context存储一个ArrayMap<BroadcastReceiver, ReceiverDispatcher> map的数据结构,这也就是一个Context可以存储多个广播,一个广播是以广播注册者为单位,可以对应多个广播派发者。

==那我们要知道==

LoadedApk.ReceiverDispatcher.getIIntentReceiver();

这个到底是什么?

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;
    mInstrumentation = instrumentation;
    mRegistered = registered;
    mLocation = new IntentReceiverLeaked(null);
    mLocation.fillInStackTrace();
}

我们通过

IIntentReceiver getIIntentReceiver() {
    return mIIntentReceiver;
}

得到的就是ReceiverDispatcher构造中创建的这个:

 mIIntentReceiver = new InnerReceiver(this, !registered);

我们好奇这个对象干啥

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;
    }

通过构造和类的声明我们知道目的是包装了LoadedApk.ReceiverDispatcher,我们记得之前传递的时false,现在这里就是true,所以保存的强弱引用都有。并且这个时Binder对象。

也就是说getReceiverDispatcher将这个对象返回出去。传递到了AMS中。

return ActivityManagerNative.getDefault().registerReceiver(
        mMainThread.getApplicationThread(), mBasePackageName,
        rd, filter, broadcastPermission, userId);

所以传递进去的参数有:

  • ActivityThread
  • 包名
  • Dispatcher
  • IntentFilter
  • broadcastPermission
  • userId

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) {
                callerApp = getRecordForAppLocked(caller);
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }
            //得到userid
            userId = handleIncomingUser(callingPid, callingUid, userId,
                    true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
            //遍历filter的action
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }
            //所有都会有一个actions列表
            
            //得到userId列表
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            //遍历所有的actions,对于每一个action遍历所有的id
            //并且从粘连广播集合中根据id获取一个以Activity对应多个Intent的列表
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
                    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);
                        }
                    }
                }
            }            
            //这块也就是说,如果有粘连广播,并且粘连广播有intent则将这些intents添加到粘连广播列表集合中。
            
            
            //小结就是:
            //根据filter得到这条广播所有的actions
            //根据actions得到每一条action
            //然后看看总体的粘连广播列表中有没有对应的action如果有,则将所有的intent遍历出来,添加到一个缓存集合中。
            
            
            //也就是说如果mStickyBroadcasts有当前记录的action则将其缓存到stickyIntents的集合中
            
            
            
            
            //这块代码时粘连广播intent将其将其与filter进行匹配将匹配到的intent添加到一个缓存中去(allSticky)
            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);
                    }
                }
            }
            //到了这一步allSticky就存储有关当前这个广播对应的所有intent
            Intent sticky = allSticky != null ? allSticky.get(0) : null; 如果intent的话,拿出第一个intent赋给sticky,否则就是null
            
            synchronized (this) {
                if (callerApp != null && (callerApp.thread == null
                        || callerApp.thread.asBinder() != caller.asBinder())) {
                    return null;
                }
                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());   
                //从一个HashMap<IBinder, ReceiverList>中看这个receiver有木有ReceiverList
                //如果木有则进行添加到这个列表中,并且注册死亡回调。
                 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.asBinder().linkToDeath(rl, 0);
                        } catch (RemoteException e) {
                            return sticky;
                        }
                        rl.linkedToDeath = true;
                    }
                    mRegisteredReceivers.put(receiver.asBinder(), rl);
                }
                
                //将filter进行封装,并且添加到ReceiverList中
            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);

            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            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();
                }
            }
            return sticky;
}

这里需要清楚数据结构的关系

如果第一次进入则:

rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver);
rl.app.receivers.add(rl);

也就是将生成的rl添加到调用者的ProcessRecord的ArraySet<ReceiverList> receivers

然后将这个ReceiverList添加到AMS的HashMap<IBinder, ReceiverList> mRegisteredReceivers,也就是ReceiverList,在那个app进程中有记录,在AMS中也有记录.于此同时mReceiverResolver中有对应的BroadcastFilter,这个BroadcastFilter也在那个app进程中也有.

最后面是将匹配到的粘连广播发送,所以粘连广播时在广播注册的时候发送的.

也就是说,只要有一个广播注册,就会匹配看是不粘连广播,如果是粘连则将粘连广播发送给这个接收者.

清楚这些数据结构之间的关系

final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
    ReceiverList  rl = new ReceiverList(this, callerApp, callingPid, callingUid,userId, receiver);
    mRegisteredReceivers.put(receiver.asBinder(), rl);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId);
rl.add(bf);

也就是说,mRegisteredReceivers中是根据receiver.asBinder()进行匹配的,然后对应一个集合,这个集合存储的时一个BroadcastFilter列表.说明一个接收者对应多个Filter.

==我们在看看ContextImpl中==

在LoadedApk.getReceiverDispatcher()中

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

这个是以BroadcastReceiver为键ReceiverDispatcher为值

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

推荐阅读更多精彩内容

  • Android插件化基础的主要内容包括 Android插件化基础1-----加载SD上APKAndroid插件化基...
    隔壁老李头阅读 4,511评论 2 35
  • 简介 广播作为Android系统四大组件之一,起得作用是相当大,安卓系统进程之间通信是相当频繁,而且解耦工作是重中...
    我叫王菜鸟阅读 6,151评论 0 2
  • 本文首发于微信公众号——世界上有意思的事,搬运转载请注明出处,否则将追究版权责任。微信号:a1018998632,...
    何时夕阅读 21,196评论 13 49
  • 2017年9月20日如是家人黄愈惠,种种子第51天。 发心:我今不是为了我个人闻思修行,而是为了六道轮回一切如母有...
    愈惠阅读 180评论 4 4
  • 近期阅读李笑来的专栏《通往财富自由之路》纸质版,每天上下班的地铁上,出差的飞机上反复啃,反复琢磨,加深印象,每次读...
    菬你这么说阅读 198评论 2 3