Broadcast 分析 --- 之二

来源:互联网 发布:mac电磁阀说明书 编辑:程序博客网 时间:2024/05/21 19:50

3,发送广播

对应不同的广播,发送方法如下:

public void sendBroadcast(Intent intent, String receiverPermission)public void sendOrderedBroadcast(Intent intent, String receiverPermission)public void sendStickyBroadcast(Intent intent)

ContextImpl的sendBroadcast调用流程图如下,


sendBroadcast方法如下,

public void sendBroadcast(Intent intent) {        warnIfCallingFromSystemProcess();        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());        try {            intent.prepareToLeaveProcess();            ActivityManagerNative.getDefault().broadcastIntent(           mMainThread.getApplicationThread(), intent, resolvedType, null,          Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,                    getUserId());        } catch (RemoteException e) {            throw new RuntimeException("Failure from system", e);        }    }
当应用apk发起一个广播时,最后都会通过跨进程调用到AMS的broadcastIntent方法,只是有些标志(是否是顺序广播)不一样而已。

broadcastIntent方法如下,

public final int broadcastIntent(IApplicationThread caller,            Intent intent, String resolvedType, IIntentReceiver resultTo,            int resultCode, String resultData, Bundle resultExtras,            String[] requiredPermissions, int appOp, Bundle options,            boolean serialized, boolean sticky, int userId) {        enforceNotIsolatedCaller("broadcastIntent");        synchronized(this) {            intent = verifyBroadcastLocked(intent);            final ProcessRecord callerApp = getRecordForAppLocked(caller);            final int callingPid = Binder.getCallingPid();            final int callingUid = Binder.getCallingUid();            final long origId = Binder.clearCallingIdentity();            int res = broadcastIntentLocked(callerApp,                    callerApp != null ? callerApp.info.packageName : null,                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,                    requiredPermissions, appOp, null, serialized, sticky,                    callingPid, callingUid, userId);            Binder.restoreCallingIdentity(origId);            return res;        }}
broadcastIntentLocked方法又臭又长,其他的就不多说了,看主要的,

private final int broadcastIntentLocked(){    List receivers = null; // 记录静态广播    List<BroadcastFilter> registeredReceivers = null; // 记录动态广播          // 根据intent 获取匹配的静态广播    receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);          // 根据intent 获取匹配的动态广播     registeredReceivers = mReceiverResolver.queryIntent(intent,                           resolvedType, false, userId);if (!ordered && NR > 0) {       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);      final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);            if (!replaced) {                queue.enqueueParallelBroadcastLocked(r);                queue.scheduleBroadcastsLocked(); // 发送动态广播            }            registeredReceivers = null;            NR = 0;        }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);            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);            if (!replaced) {                queue.enqueueOrderedBroadcastLocked(r);                queue.scheduleBroadcastsLocked();  // 发送静态广播            }        }}
由于广播分为动态广播和静态广播,所以在该函数中,分别定义了2个list来查询并保存匹配的静态和动态广播,最后分别插入广播队列进行处理。

3.1 查询

3.1.1静态广播的匹配查询

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;                }                List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()                        .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);                if (user != UserHandle.USER_OWNER && newReceivers != null) {                   •••                    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;    }

AppGlobals.getPackageManager()方法最后会得到PMS,直接看queryIntentReceivers方法,

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

一般广播都是动态广播,而有序广播两种状态都有,有序广播保存receivers 序列中。首先查询符合条件的静态广播

receivers =collectReceiverComponents(intent,

AppGlobals.getPackageManager().queryIntentReceivers(intent

然后将receivers 和registeredReceivers根据数据的大小(优先级)合并到receivers中,最后插入队列并发起实际的广播调度。

   queue.enqueueOrderedBroadcastLocked(r);

  queue.scheduleBroadcastsLocked();

结构图如下:


3.1.2动态广播的匹配查询

查询到符合条件的动态广播之后,根据BroadcastFilter创建一个BroadcastRecord节点,插入BroadcastQueue内的并行处理队列,

最后发起实际的广播调度。

结构图如下:


3.2 一般广播执行流程

3.2.1流程图


3.2.1 关键代码解析

一般广播里面的所有广播同时全部执行,最后调用onReceive函数,通过finishReceiver反馈结果。在processNextBroadcast函数中,

可以看到同时调用,

while (mParallelBroadcasts.size() > 0) {    r = mParallelBroadcasts.remove(0);    r.dispatchTime = SystemClock.uptimeMillis();    r.dispatchClockTime = System.currentTimeMillis();    final int N = r.receivers.size();    . . . . . .     for (int i=0; i<N; i++)     {        Object target = r.receivers.get(i);        . . . . . .        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);    }    . . . . . .}

3.3 有序广播

3.3.1流程图

 

3.3.1 关键代码分析

在processNextBroadcast函数中,首先判断该app是否为空,说明该进程还没有起来,那就先调用processCurBroadcastLocked启动app,

最后都会调用processCurBroadcastLocked执行,调用onReceive处理广播。

因为是有序广播,是一个一个安装顺序执行,在反馈的结果BroadcastReceive中,会根据相关信息调用processNextBroadcast函数接着

执行下一个广播。

广播完成之后和AMS还有一个交互,其实有序广播就比一般广播多了一个重复的过程,相当于多条广播逐条执行。