说说Android的广播(3)

什么样的广播是并发的?

现在让我们开始破解Android中的一个trick,普通广播都是并发的吗?
带着这个问题,我们来看ActivityManagerService.broadcastIntentLocked中的实现逻辑。

broadcastIntentLocked中的细节很多,我们放到后面讲,我们先只把跟并发和串行队列有关的部分专门提炼出来。

receivers和registeredReceivers

画重点了,画重点了啊!下面我们要学习两个重要的概念,一个是receivers,另一个是registeredReceivers。这两个东西的区别真正决定了它是能并发接收到广播还是串行地接收广播。

通俗地讲,registeredReceivers就是通过Java代码在运行时注册的receiver,而receivers是连通过AndroidManifest.xml中注册的也算上。
如果想只发给动态注册的BroadcastReceiver,可以设置有个FLAG_RECEIVER_REGISTERED_ONLY属性。

在处理普通广播处理的流程时,就先定义了两个List,一个是receivers,另一个是registeredReceivers.

16820        // Figure out who all will receive this broadcast.
16821        List receivers = null;
16822        List<BroadcastFilter> registeredReceivers = null;

我们分别看看,它们都是在哪里被赋值的:

如刚才所说,只有在不设FLAG_RECEIVER_REGISTERED_ONLY的情况下,才需要查完整的receiver列表。

16823        // Need to resolve the intent to interested receivers...
16824        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
16825                 == 0) {
16826            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
16827        }

如果intent.getComponent() == null,则通过queryIntent方法来查询registeredReceivers。

16828        if (intent.getComponent() == null) {
...
16847                registeredReceivers = mReceiverResolver.queryIntent(intent,
16848                        resolvedType, false, userId);
...
16850        }

好,两个receiver相关的列表都被赋值好了。下面揭晓奇迹的时候来了,究竟是哪个被翻牌子了呢?

16858        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
16859        if (!ordered && NR > 0) {
16860            // If we are not serializing this broadcast, then send the
16861            // registered receivers separately so they don't wait for the
16862            // components to be launched.
16863            final BroadcastQueue queue = broadcastQueueForIntent(intent);
16864            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
16865                    callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
16866                    appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
16867                    resultExtras, ordered, sticky, false, userId);
16868            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
16869            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
16870            if (!replaced) {
16871                queue.enqueueParallelBroadcastLocked(r);
16872                queue.scheduleBroadcastsLocked();
16873            }
16874            registeredReceivers = null;
16875            NR = 0;
16876        }

没错,是非ordered的registered receivers被并发执行了。

那么receivers是如何被处理的呢?一句话,被当成ordered处理了.
我们看代码:

16950        if ((receivers != null && receivers.size() > 0)
16951                || resultTo != null) {
16952            BroadcastQueue queue = broadcastQueueForIntent(intent);
16953            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
16954                    callerPackage, callingPid, callingUid, resolvedType,
16955                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
16956                    resultData, resultExtras, ordered, sticky, false, userId);
...
16965                queue.enqueueOrderedBroadcastLocked(r);
16966                queue.scheduleBroadcastsLocked();
...
16968        }

ActivityManagerService.broadcastIntentLocked完整流程分析

下面我们就要正式开始看后面的处理逻辑了。先给大家来看一张时序图,整体上先有一个印象:

sequenceDiagram;
    Activity ->> ContextWrapper : sendBroadcast()
    ContextWrapper ->> ContextImpl : sendBroadcast()
    ContextImpl ->> ActivityManagerService: broadcastIntent()
    ActivityManagerService ->> ActivityManagerService : broadcastIntentLocked()
    ActivityManagerService ->> ActivityManagerService : collectReceiverComponents()
    ActivityManagerService ->> BroadcastQueue : scheduleBroadcastsLocked()
    BroadcastQueue ->> BroadcastQueue : processNextBroadcast()
    ActivityManagerService ->> ActivityManagerService : deliverToRegisteredReceiverLocked()
    ActivityManagerService ->> ActivityManagerService : performReceiveLocked()
    ActivityManagerService ->> ApplicationThreadProxy : scheduleRegisteredReceiver()
    ApplicationThreadProxy ->> InnerReceiver : performReceive()
    InnerReceiver ->> ReceiverDispatcher : performReceive()
    ReceiverDispatcher ->> BroadcastReceiver : onReceive()

上面我们是跳着看的,大家缺少一个完整的印象,让我们耐着性子把整个流程过一下吧。

private final int broadcastIntentLocked(ProcessRecord callerApp,
        String callerPackage, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,
        boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
    intent = new Intent(intent);

    // By default broadcasts do not go to stopped apps.
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

    // If we have not finished booting, don't allow this to launch new processes.
    if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    }

    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
            (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
            + " ordered=" + ordered + " userid=" + userId);
    if ((resultTo != null) && !ordered) {
        Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
    }

    userId = handleIncomingUser(callingPid, callingUid, userId,
            true, ALLOW_NON_FULL, "broadcast", callerPackage);
...

中间是一堆判断权限之类的操作,我们就略过不看了。

...
    final String action = intent.getAction();
    if (action != null) {
        switch (action) {
            case Intent.ACTION_UID_REMOVED:
            case Intent.ACTION_PACKAGE_REMOVED:
            case Intent.ACTION_PACKAGE_CHANGED:
            case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
            case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
...
            case Intent.ACTION_TIMEZONE_CHANGED:
                // If this is the time zone changed action, queue up a message that will reset
                // the timezone of all currently running processes. This message will get
                // queued up before the broadcast happens.
                mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
                break;
            case Intent.ACTION_TIME_CHANGED:
                // If the user set the time, let all running processes know.
                final int is24Hour =
                        intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1
                                : 0;
                mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
                BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
                synchronized (stats) {
                    stats.noteCurrentTimeChangedLocked();
                }
                break;
            case Intent.ACTION_CLEAR_DNS_CACHE:
                mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
                break;
            case Proxy.PROXY_CHANGE_ACTION:
                ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
                mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
                break;
        }
    }
...

上面是一些特殊的广播的处理。
下面终于开始比较核心的逻辑了,去查找一下接收这个广播的接收者都是些什么吧。

    // Figure out who all will receive this broadcast.
    List receivers = null;
    List<BroadcastFilter> registeredReceivers = null;
    // Need to resolve the intent to interested receivers...
    if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
             == 0) {
        receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
    }

FLAG_RECEIVER_REGISTERED_ONLY这个属性用于说明是否是支持在代码中动态注册的接收者,如果不是的话,那么要通过collectReceiverComponents方法去查一下哪些应用在AndroidManifest.xml中也注册了监听。

这么有趣的函数,我们跟起去看看吧。

ActivityManagerService.collectReceiverComponents

进入之前,我们先看看几个数据类的定义:
ResolveInfo类用来存储AndroidManifest.xml中<intent>标签中对应的信息。

public class ResolveInfo implements Parcelable;

我们正式开始进入collectReceiverComponents:

private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,int callingUid, int[] users) {
    List<ResolveInfo> receivers = null;
    try {
        HashSet<ComponentName> singleUserReceivers = null;
        boolean scannedFirstReceivers = false;
        for (int user : users) {
            // Skip users that have Shell restrictions
            if (callingUid == Process.SHELL_UID
                    && getUserManagerLocked().hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
                continue;
            }

下面调用到PMS去查询:

            List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                    .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);

PMS中的这段代码是这样的,我们先不再往下细分析了,后面会讲注册的时候再提到。

public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
        int userId) {
    if (!sUserManager.exists(userId)) return Collections.emptyList();
    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 null;
    }
}

我们回到collectReceiverComponents中继续往下看:

            if (user != UserHandle.USER_OWNER && newReceivers != null) {
                // If this is not the primary user, we need to check for
                // any receivers that should be filtered out.
                for (int i=0; i<newReceivers.size(); i++) {
                    ResolveInfo ri = newReceivers.get(i);
                    if ((ri.activityInfo.flags&ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {
                        newReceivers.remove(i);
                        i--;
                    }
                }
            }
            if (newReceivers != null && newReceivers.size() == 0) {
                newReceivers = null;
            }
            if (receivers == null) {
                receivers = newReceivers;
            } else if (newReceivers != null) {
                // We need to concatenate the additional receivers
                // found with what we have do far.  This would be easy,
                // but we also need to de-dup any receivers that are
                // singleUser.
                if (!scannedFirstReceivers) {
                    // Collect any single user receivers we had already retrieved.
                    scannedFirstReceivers = true;
                    for (int i=0; i<receivers.size(); i++) {
                        ResolveInfo ri = receivers.get(i);
                        if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
                            ComponentName cn = new ComponentName(
                                    ri.activityInfo.packageName, ri.activityInfo.name);
                            if (singleUserReceivers == null) {
                                singleUserReceivers = new HashSet<ComponentName>();
                            }
                            singleUserReceivers.add(cn);
                        }
                    }
                }
                // Add the new results to the existing results, tracking
                // and de-dupping single user receivers.
                for (int i=0; i<newReceivers.size(); i++) {
                    ResolveInfo ri = newReceivers.get(i);
                    if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
                        ComponentName cn = new ComponentName(
                                ri.activityInfo.packageName, ri.activityInfo.name);
                        if (singleUserReceivers == null) {
                            singleUserReceivers = new HashSet<ComponentName>();
                        }
                        if (!singleUserReceivers.contains(cn)) {
                            singleUserReceivers.add(cn);
                            receivers.add(ri);
                        }
                    } else {
                        receivers.add(ri);
                    }
                }
            }
        }
    } catch (RemoteException ex) {
        // pm is in same process, this will never happen.
    }
    return receivers;
}

下面,我们回到broadcastIntentLocked中继续往下看:

    if (intent.getComponent() == null) {
        if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
            // Query one target user at a time, excluding shell-restricted users
            UserManagerService ums = getUserManagerLocked();
            for (int i = 0; i < users.length; i++) {
                if (ums.hasUserRestriction(
                        UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                    continue;
                }
                List<BroadcastFilter> registeredReceiversForUser =
                        mReceiverResolver.queryIntent(intent,
                                resolvedType, false, users[i]);
                if (registeredReceivers == null) {
                    registeredReceivers = registeredReceiversForUser;
                } else if (registeredReceiversForUser != null) {
                    registeredReceivers.addAll(registeredReceiversForUser);
                }
            }
        } else {
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                    resolvedType, false, userId);
        }
    }

    final boolean replacePending =
            (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

终于快到我们最关心的逻辑部分了,大家仔细看啦!

    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()
            + " replacePending=" + replacePending);

    int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
    if (!ordered && NR > 0) {
        // If we are not serializing this broadcast, then send the
        // registered receivers separately so they don't wait for the
        // components to be launched.

如果不是ordered,且注册这个事件的receiver队列不为空,我们就开始处理普通消息了!

        final BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                resultExtras, ordered, sticky, false, userId);
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
        final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);

下面我们就将普通消息加入到并行消息队列里,enqueueParallelBroadcastLocked和scheduleBroadcastsLocked我们前面预备知识都讲过了,这里不再重复了。

        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
        registeredReceivers = null;
        NR = 0;
    }

以上,并发消息处理正式结束。下面开始的是串行消息的处理,中间我们略去一段对于PACKAGE_ADDED消息发给新安装的应用这一段支线任务。

另外,有序队列中还有一件额外的事情需要做,有序么,要排序。

    // Merge into one list.
    int ir = 0;
    if (receivers != null) {
...
        int NT = receivers != null ? receivers.size() : 0;
        int it = 0;
        ResolveInfo curt = null;
        BroadcastFilter curr = null;
        while (it < NT && ir < NR) {
            if (curt == null) {
                curt = (ResolveInfo)receivers.get(it);
            }
            if (curr == null) {
                curr = registeredReceivers.get(ir);
            }
            if (curr.getPriority() >= curt.priority) {
                // Insert this broadcast record into the final list.
                receivers.add(it, curr);
                ir++;
                curr = null;
                it++;
                NT++;
            } else {
                // Skip to the next ResolveInfo in the final list.
                it++;
                curt = null;
            }
        }
    }
    while (ir < NR) {
        if (receivers == null) {
            receivers = new ArrayList();
        }
        receivers.add(registeredReceivers.get(ir));
        ir++;
    }

最后是加入到串行队列并发送消息的过程,跟并发队列除了队列不同,其余都是一样的。

    if ((receivers != null && receivers.size() > 0)
            || resultTo != null) {
        BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, resolvedType,
                requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                resultData, resultExtras, ordered, sticky, false, userId);

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                + ": prev had " + queue.mOrderedBroadcasts.size());
        if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                "Enqueueing broadcast " + r.intent.getAction());

        boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
        if (!replaced) {
            queue.enqueueOrderedBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
    }

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

推荐阅读更多精彩内容