iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >ActivityManagerService广播并行发送与串行发送怎么实现
  • 171
分享到

ActivityManagerService广播并行发送与串行发送怎么实现

2023-07-05 07:07:45 171人浏览 泡泡鱼
摘要

这篇文章主要讲解了“ActivityManagerService广播并行发送与串行发送怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ActivityManagerService广播并

这篇文章主要讲解了“ActivityManagerService广播并行发送与串行发送怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ActivityManagerService广播并行发送与串行发送怎么实现”吧!

"并行"广播的发送

本文以 ActivityManagerService之广播(1): 注册与发送 为基础,分析“串行”和“并行”广播的发送流程,并介绍广播 ANR 的原理。

// 1. 获取广播队列final BroadcastQueue queue = broadcastQueueForIntent(intent);// 2. 创建广播记录BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,        callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,        requiredPermissions, excludedPermissions, appOp, brOptions, reGISteredReceivers,        resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId,        allowBackgroundActivityStarts, backgroundActivityStartsToken,        timeoutExempt);// 3. 广播记录加入到并行队列中queue.enqueueParallelBroadcastLocked(r);// 4. 调度发送广播queue.scheduleBroadcastsLocked();

第3步,把广播记录保存到并行队列中

// BroadcastQueue.javapublic void enqueueParallelBroadcastLocked(BroadcastRecord r) {    // mParallelBroadcasts 类型为 ArrayList<BroadcastRecord>    mParallelBroadcasts.add(r);    enqueueBroadcastHelper(r);}

第4步,调度发送广播,最终会调用如下方法

// BroadcastQueue.java// 此时,参数 fromMsg 为 true,skipOomAdj 为 falsefinal void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    BroadcastRecord r;    mService.updateCpuStats();    if (fromMsg) {        mBroadcastsScheduled = false;    }    // 遍历"并行"广播队列    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, i);        }        addBroadcastToHistoryLocked(r);    }    // ... 省略"串行"广播的发送 ...}

虽然名为“并行”广播,但是仍然是从队列取出广播,然后逐个发送给动态接收器。很显然,这里的行为与“并行”的含义并不一致?那么广播的“并行”发送究竟是什么意思?

接着看“并行”广播如何发送给动态接收器的

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,        BroadcastFilter filter, boolean ordered, int index) {    // ... 省略一大堆的权限或者异常检测 ...    // 一个广播可能有多个接收者,因此需要一个数组来保存发送的状态    r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;    // ordered 目前为 false    if (ordered) {        // ...        }    } else if (filter.receiverList.app != null) {        // 马上要发送广播给接收方,因此要暂时解冻接收方的进程        mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app);    }    try {        if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {            // ... 处于备份状态中 ...        } else {            r.receiverTime = SystemClock.uptimeMillis();            // 保存允许从后台启动activity的token            maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);            // 添加到省电模式白名单中            maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);            // 执行广播的发送            perfORMReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,                    new Intent(r.intent), r.resultCode, r.resultData,                    r.resultExtras, r.ordered, r.initialSticky, r.userId);            // parallel broadcasts are fire-and-forget, not bookended by a call to            // finishReceiverLocked(), so we manage their activity-start token here            if (filter.receiverList.app != null                    && r.allowBackgroundActivityStarts && !r.ordered) {                postActivityStartTokenRemoval(filter.receiverList.app, r);            }        }        // ordered 目前为 false        if (ordered) {            r.state = BroadcastRecord.CALL_DONE_RECEIVE;        }    } catch (RemoteException e) {        // ...    }}

抛开一些细节,直接看 performReceiveLocked()

// BroadcastQueue.javavoid performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,        Intent intent, int resultCode, String data, Bundle extras,        boolean ordered, boolean sticky, int sendingUser)        throws RemoteException {    // 动态广播接收器的进程,应该是存在的    if (app != null) {        final IApplicationThread thread = app.getThread();        if (thread != null) {            try {                // 发送广播给接收方进程                thread.scheduleRegisteredReceiver(receiver, intent, resultCode,                        data, extras, ordered, sticky, sendingUser,            } catch (RemoteException ex) {               // ...            }        } else {            throw new RemoteException("app.thread must not be null");        }    } else {        // ...    }}

很简单,就是通过进程 attach 的 IApplicationThread 接口,发送广播给进程。这个过程,暂时先不分析,后面会分析到。

那么,现在来回答一下,何为“并行”广播?其实这个答案,我也是对比了串行广播的发送过程,才得出来的。所谓的"并行"发送,实际上就是把广播逐个发送给动态接收器,但是不需要等待前一个接收器反馈处理结果,就可以发送下一个。而“串行”广播的发送,是需要等待前一个广播接收器反馈处理结果后,才能调度发送下一个广播。

“串行”广播的发送

// 1.获取广播队列BroadcastQueue queue = broadcastQueueForIntent(intent);// 2.创建广播记录BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,        callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,        requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,        receivers, resultTo, resultCode, resultData, resultExtras,        ordered, sticky, false, userId, allowBackgroundActivityStarts,        backgroundActivityStartsToken, timeoutExempt);// 3.广播记录加入到串行队列中queue.enqueueOrderedBroadcastLocked(r);// 4.调度发送广播queue.scheduleBroadcastsLocked();

第3步,广播加入到串行队列中

// BroadcastQueue.javapublic void enqueueOrderedBroadcastLocked(BroadcastRecord r) {    mDispatcher.enqueueOrderedBroadcastLocked(r);    enqueueBroadcastHelper(r);}
// BroadcastDispatcher.javavoid enqueueOrderedBroadcastLocked(BroadcastRecord r) {    mOrderedBroadcasts.add(r);}

并行发送的广播保存到 BroadcastQueue#mParallelBroadcasts 中,而串行发送的广播保存到 BroadcastDispatcher#mOrderedBroadcasts 中,为何要这样设计呢?有兴趣的读者可以研究下。

第4步,“串行”广播的调度发送,仍然使用的是 processNextBroadcastLocked() 方法,但是代码量是非常的大,下面将把函数分段解析。

processNextBroadcastLocked() 函数有400多行代码,这个函数里有很多东西都可以抽出来的,但是随着版本的更新,这块代码一直没有优化过。

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    BroadcastRecord r;    mService.updateCpuStats();    if (fromMsg) {        mBroadcastsScheduled = false;    }    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // 检测 receiver 进程是否死亡        boolean isDead;        if (mPendingBroadcast.curApp.getPid() > 0) {            synchronized (mService.mPidsSelfLocked) {                ProcessRecord proc = mService.mPidsSelfLocked.get(                        mPendingBroadcast.curApp.getPid());                isDead = proc == null || proc.mErrorState.isCrashing();            }        } else {            final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(                    mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);            isDead = proc == null || !proc.isPendingStart();        }        if (!isDead) {            // 进程仍然存活,结束此次广播的处理流程,继续等待            // 等待什么呢?等待广播进程起来,并与 AMS 完成 attach application            // 在 attach application 的过程中,会完成广播的发送            return;        } else {            // 进程死亡,继续处理下一个广播            mPendingBroadcast.state = BroadcastRecord.IDLE;            mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;            mPendingBroadcast = null;        }    }

当发送一个广播给 receiver 时,如果 receiver 进程没有启动,那么会先 fork 一个 receiver 进程,然后用 mPendingBroadcast 保存待发送的广播。当 receiver 进程起来的时候,会与 AMS 执行 attach application 过程,在这个过程中,会自动把 mPendingBroadcast 保存的广播发送给 receiver 进程。

因此,这里检测到 mPendingBroadcast 不为 null 时,那么 receiver 进程肯定在启动中,只要 receiver 进程没有死亡,就什么也不用做,因为广播会自动发送给 receiver 进程。

接着看下一步的处理

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    // ...    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // ...    }    boolean looped = false;    // 2. 通过 do-while 循环,找到一个现在可以处理的广播    do {        final long now = SystemClock.uptimeMillis();        // 获取一个待处理的广播        r = mDispatcher.getNextBroadcastLocked(now);        if (r == null) {            // ... 没有广播需要处理 ...            return;        }        boolean forceReceive = false;        // 处理严重超时的广播,有两种情况        // 一种情况是,在系统还没有起来前,发送的广播得不到执行,发生严重超时        // 另外一种情况是,在系统起来后,有一些超时豁免的广播,发生了严重超时        int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;        if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {            if ((numReceivers > 0) &&                    (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {                broadcastTimeoutLocked(false); // forcibly finish this broadcast                forceReceive = true;                r.state = BroadcastRecord.IDLE;            }        }        if (r.state != BroadcastRecord.IDLE) {            return;        }        // 当前广播因为某种原因,终止处理,然后处理下一个广播         if (r.receivers == null || r.nextReceiver >= numReceivers                || r.resultAbort || forceReceive) {            // ...            // 通知 BroadcastDispatcher ,不处理这个广播了            mDispatcher.retireBroadcastLocked(r);            r = null;            looped = true;            // 下一次循环,获取下一个广播来处理            continue;        }        // 处理推迟发送广播的情况        if (!r.deferred) {            final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));            if (mDispatcher.isDeferringLocked(receiverUid)) {                // ...                // 保存推迟发送的广播                mDispatcher.aDDDeferredBroadcast(receiverUid, defer);                r = null;                looped = true;                // 下一次循环时,获取下一个广播来处理                continue;            }        }    } while (r == null);

先从整体看,通过一个 do-while 循环,最终是为了找到下一个处理的广播。为何要用一个循环来寻找呢? 因为广播可能没有接收器,或者已经严重超时,又或者广播需要推迟发送。所以要通过一个循环,找到一个能立即发送的广播。

由于本文主要是为了分析广播发送的整体流程,对于有些细节,只做注释而不做细致分析。需要深入研究的读者,可以在本文的基础上继续分析。

继续接着看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    BroadcastRecord r;    mService.updateCpuStats();    if (fromMsg) {        mBroadcastsScheduled = false;    }    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // ...    }    boolean looped = false;    // 2. 通过 do-while 循环,找到一个现在可以处理的广播    do {        final long now = SystemClock.uptimeMillis();        // 获取一个待处理的广播        r = mDispatcher.getNextBroadcastLocked(now);        // ...    } while (r == null);    // 走到这里,表示已经获取了一个现在可以处理的广播    int recIdx = r.nextReceiver++;    // 3. 在发送广播之前,先发送一个超时消息    r.receiverTime = SystemClock.uptimeMillis();    if (recIdx == 0) {        // 在广播开始发送给第一个接收器时,记录发送的时间        r.dispatchTime = r.receiverTime;        r.dispatchClockTime = System.currentTimeMillis();    }    if (! mPendingBroadcastTimeoutMessage) {        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;        setBroadcastTimeoutLocked(timeoutTime);    }

在广播发送给一个 receiver 之前,会先发送一个超时消息。从广播准备发送给一个 receiver 算起,到 receiver 处理完广播,并反馈给 AMS,如果这个时间段超过了一个时间阈值,就会引发 ANR。触发 ANR 的代码设计非常巧妙,后面会具体分析这个过程。

接着看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    // ...    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // ...    }    boolean looped = false;    // 2. 通过 do-while 循环,找到一个现在可以处理的广播    do {        // ...    } while (r == null);    int recIdx = r.nextReceiver++;    // ...    // 3. 在发送广播之前,先发送一个超时消息    // 当广播处理超时时,会触发 ANR    if (! mPendingBroadcastTimeoutMessage) {        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;        setBroadcastTimeoutLocked(timeoutTime);    }    final BroadcastOptions brOptions = r.options;    // 4. 获取一个 receiver    final Object nextReceiver = r.receivers.get(recIdx);    // 5. 如果这个接收器是动态接收器,先把广播发送给它    // 注意,这里处理的是有序广播发送给动态接收器的情况    if (nextReceiver instanceof BroadcastFilter) {        BroadcastFilter filter = (BroadcastFilter)nextReceiver;        // 发送广播给动态接收器        deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);        if (r.receiver == null || !r.ordered) {            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["                    + MQueueName + "]: ordered="                    + r.ordered + " receiver=" + r.receiver);            r.state = BroadcastRecord.IDLE;            scheduleBroadcastsLocked();        } else {            if (filter.receiverList != null) {                maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);            }        }        // 注意,把广播发送给 动态receiver 后,直接返回        return;    }

现在一切就绪,那么开始获取一个 receiver,当这个 receiver 是一个动态接收器时,直接发送广播给它,这个发送过程前面已经分析过。

注意,这里处理的情况是,把有序广播发送给动态接收器。并且发送完成后,直接 return, 也就是结束了此次广播的发送流程。

一个广播可能有多个接收器,为何这里只发送给一个动态接收器,就直接返回了? 这就是从“串行”广播的本质,需要等待当前的广播接收器处理完广播,并返回结果后,才能把广播发送给下一个广播接收器。

接着看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    // ...    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // ...    }    boolean looped = false;    // 2. 通过 do-while 循环,找到一个现在可以处理的广播    do {        final long now = SystemClock.uptimeMillis();        // 获取一个待处理的广播        r = mDispatcher.getNextBroadcastLocked(now);        // ...    } while (r == null);    // 走到这里,表示已经获取了一个现在可以处理的广播    int recIdx = r.nextReceiver++;    // 3. 在发送广播之前,先发送一个超时消息    r.receiverTime = SystemClock.uptimeMillis();    // ...    if (! mPendingBroadcastTimeoutMessage) {        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;        setBroadcastTimeoutLocked(timeoutTime);    }    final BroadcastOptions brOptions = r.options;    // 4. 获取一个 receiver    final Object nextReceiver = r.receivers.get(recIdx);    // 5. 如果这个接收器是动态接收器,先把广播发送给它    // 注意,这里处理的是有序广播发送给动态接收器的情况    if (nextReceiver instanceof BroadcastFilter) {        // ...        return;    }    // 走到这里,表示当前的广播接收器,是静态接收器    // 获取静态接收器的信息    ResolveInfo info =        (ResolveInfo)nextReceiver;    ComponentName component = new ComponentName(            info.activityInfo.applicationInfo.packageName,            info.activityInfo.name);    boolean skip = false;    // 6. 检测是否不需要把广播发送给静态接收器    // ... 省略一大堆的检测代码 ...    String targetProcess = info.activityInfo.processName;    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,            info.activityInfo.applicationInfo.uid);    if (!skip) {        // 检测是否允许把广播发送给静态接收器        final int allowed = mService.getAppStartModeLOSP(                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,                info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);        // 例如,大于等于 O+ 版本的 app ,不允许广播发送给静态接收器        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {            // ephemeral app 会返回这个模式            if (allowed == ActivityManager.APP_START_MODE_DISABLED) {                skip = true;            } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)                    || (r.intent.getComponent() == null                        && r.intent.getPackage() == null                        && ((r.intent.getFlags()                                & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)                        && !isSignaturePerm(r.requiredPermissions))) {                // 打破以上任意一个条件,即可把广播发送给静态接收器                mService.addBackgroundCheckViolationLocked(r.intent.getAction(),                        component.getPackageName());                skip = true;            }        }    }    // 跳过当前广播的发送    if (skip) {        // ...        return;    }

如果这个 reciever 是静态接收器,那么在把广播发送给它之前,首先得进行一大堆的检测。最常见的就是权限,但是这里展示了一段 Android O+ 限制广播发送给静态接收器的限制,有兴趣的读者可以详细分析。

接着看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    // ...    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // ...    }    boolean looped = false;    // 2. 通过 do-while 循环,找到一个现在可以处理的广播    do {        final long now = SystemClock.uptimeMillis();        // 获取一个待处理的广播        r = mDispatcher.getNextBroadcastLocked(now);        // ...    } while (r == null);    // 走到这里,表示已经获取了一个现在可以处理的广播    int recIdx = r.nextReceiver++;    // 3. 在发送广播之前,先发送一个超时消息    r.receiverTime = SystemClock.uptimeMillis();    // ...    if (! mPendingBroadcastTimeoutMessage) {        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;        setBroadcastTimeoutLocked(timeoutTime);    }    final BroadcastOptions brOptions = r.options;    // 4. 获取一个 receiver    final Object nextReceiver = r.receivers.get(recIdx);    // 5. 如果这个接收器是动态接收器,先把广播发送给它    // 注意,这里处理的是有序广播发送给动态接收器的情况    if (nextReceiver instanceof BroadcastFilter) {        // ...        return;    }    // 走到这里,表示当前的广播接收器,是静态接收器    ResolveInfo info =        (ResolveInfo)nextReceiver;    ComponentName component = new ComponentName(            info.activityInfo.applicationInfo.packageName,            info.activityInfo.name);    boolean skip = false;    // 6. 检测是否不需要把广播发送给静态接收器    // ... 省略一大堆的检测代码 ...    String targetProcess = info.activityInfo.processName;    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,            info.activityInfo.applicationInfo.uid);    // ...    // 跳过当前广播的发送    if (skip) {        // ...        return;    }    // 现在可以把广播发送给静态接收器了    // ...    // 7. 静态接收器的进程正在运行,那么就把广播发送给它    if (app != null && app.getThread() != null && !app.isKilled()) {        try {            app.addPackage(info.activityInfo.packageName,                    info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);            maybeAddAllowBackgroundActivityStartsToken(app, r);            // 发送广播给广播进程            processCurBroadcastLocked(r, app);            // 注意,广播发送给这个静态接收器后,直接结束此次广播的处理            return;        } catch (RemoteException e) {            // ...        }    }}

如果没有限制,那么现在就可以把广播发送给静态接收器。

如果静态接收器所在的进程已经运行了,那么把广播发送给这个进程,这个过程与前面发送广播给动态接收器的过程非常类似,这里就不分析了。

注意,这里把广播发送给一个静态接收器,也是直接 return,懂了吧?

接着往下看

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {    // ...    // 把并行广播发送给动态接收器    while (mParallelBroadcasts.size() > 0) {        // ...    }    // 1. 处理 receiver 进程正在启动的情况    if (mPendingBroadcast != null) {        // ...    }    boolean looped = false;    // 2. 通过 do-while 循环,找到一个现在可以处理的广播    do {        final long now = SystemClock.uptimeMillis();        // 获取一个待处理的广播        r = mDispatcher.getNextBroadcastLocked(now);        // ...    } while (r == null);    // 走到这里,表示已经获取了一个现在可以处理的广播    int recIdx = r.nextReceiver++;    // 3. 在发送广播之前,先发送一个超时消息    r.receiverTime = SystemClock.uptimeMillis();    // ...    if (! mPendingBroadcastTimeoutMessage) {        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;        setBroadcastTimeoutLocked(timeoutTime);    }    final BroadcastOptions brOptions = r.options;    // 4. 获取一个 receiver    final Object nextReceiver = r.receivers.get(recIdx);    // 5. 如果这个接收器是动态接收器,先把广播发送给它    // 注意,这里处理的是有序广播发送给动态接收器的情况    if (nextReceiver instanceof BroadcastFilter) {        // ...        return;    }    // 走到这里,表示当前的广播接收器,是静态接收器    ResolveInfo info =        (ResolveInfo)nextReceiver;    ComponentName component = new ComponentName(            info.activityInfo.applicationInfo.packageName,            info.activityInfo.name);    boolean skip = false;    // 6. 检测是否不需要把广播发送给静态接收器    // ...    // 跳过当前广播的发送    if (skip) {        r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;        r.receiver = null;        r.curFilter = null;        r.state = BroadcastRecord.IDLE;        r.manifestSkipCount++;        // 发送下一个广播        scheduleBroadcastsLocked();        return;    }    // 现在可以把广播发送给静态接收器了    // ...    // 7. 静态接收器的进程正在运行,那么就把广播发送给它    if (app != null && app.getThread() != null && !app.isKilled()) {        // ...    }    // 8. 静态接收器的进程没有运行,fork it!    r.curApp = mService.startProcessLocked(targetProcess,            info.activityInfo.applicationInfo, true,            r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,            new HostingRecord("broadcast", r.curComponent), isActivityCapable            ? ZYGoTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,            (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);    // 处理 fork 进程失败的情况    if (r.curApp == null) {        // ...        return;    }    maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);    // fork 进程成功,保存广播数据,等待进程起来后,再处理这个广播    mPendingBroadcast = r;    mPendingBroadcastRecvIndex = recIdx;}

刚才已经处理了静态接收器的进程存在的情况,那么现在处理进程不存在的情况,因此首先得 fork 进程。当成功 fork 进程后,保存待发送的广播的数据,例如,用 mPendingBroadcast 保存广播,然后当进程启动时,与 AMS 进行 attach application 时,会自动把广播发送给该进程。这个过程后面会分析。

注意,此时函数已经结束,而广播正在发送给一个正在启动的进程。很显然,需要等待这个广播的处理结果,才能继续下一个广播的发送,这也符合“串行”广播的定义。

广播发送给正在启动的进程

刚才,我们分析到一个过程,当静态接收器所在的进程没有启动的时候,首先 fork 进程,那么广播之后是如何发送给进程的呢?

首先,我们知道当进程启动后,会执行 attach application 过程,最终会调用 AMS 如下方法

// ActivityManagerService.java    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,            int pid, int callingUid, long startSeq) {        // ...        try {            // ...            if (app.getIsolatedEntryPoint() != null) {                // ...            } else if (instr2 != null) {                // ...            } else {                // 初始化进程环境                thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,                        null, null, null, testMode,                        mBinderTransactionTrackingEnabled, enableTrackAllocation,                        isRestrictedBackupMode || !normalMode, app.isPersistent(),                        new Configuration(app.getWindowProcessController().getConfiguration()),                        app.getCompat(), getCommonServicesLocked(app.isolated),                        mCoreSettingsObserver.getCoreSettingsLocked(),                        buildSerial, autofillOptions, contentCaptureOptions,                        app.getDisabledCompatChanges(), serializedSystemFontMap);            }            // ...        } catch (Exception e) {            // ...        }        // ....        // 处理正在等待宿主进程起来的广播        if (!badApp && isPendingBroadcastProcessLocked(pid)) {            try {                // 发送队列中正在等待进程起来的广播                didSomething |= sendPendingBroadcastsLocked(app);                checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");            } catch (Exception e) {                // ...            }        }        // ...        return true;    }    boolean sendPendingBroadcastsLocked(ProcessRecord app) {        boolean didSomething = false;        for (BroadcastQueue queue : mBroadcastQueues) {            didSomething |= queue.sendPendingBroadcastsLocked(app);        }        return didSomething;    }

看到了,AMS 首先对进程进行了初始化,然后就会把等待进程启动的广播,发送给它。

// BroadcastQueue.javapublic boolean sendPendingBroadcastsLocked(ProcessRecord app) {    boolean didSomething = false;    // mPendingBroadcast 保存的就是等待进程启动启动后,需要发送的广播。    final BroadcastRecord br = mPendingBroadcast;    if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {        if (br.curApp != app) {            Slog.e(TAG, "App mismatch when sending pending broadcast to "                    + app.processName + ", intended target is " + br.curApp.processName);            return false;        }        try {            mPendingBroadcast = null;            // 发送广播给进程            processCurBroadcastLocked(br, app);            didSomething = true;        } catch (Exception e) {            // ...        }    }    return didSomething;}

mPendingBroadcast 保存的就是等待进程启动启动后,需要发送的广播。现在进程已经启动,立即发送广播

// BroadcastQueue.javaprivate final void processCurBroadcastLocked(BroadcastRecord r,        ProcessRecord app) throws RemoteException {    final IApplicationThread thread = app.getThread();    if (thread == null) {        throw new RemoteException();    }    if (app.isInFullBackup()) {        skipReceiverLocked(r);        return;    }    // 更新正在处理广播的 receiver 数据    r.receiver = thread.asBinder();    r.curApp = app;    // 保存当前正在运行的 receiver    final ProcessReceiverRecord prr = app.mReceivers;    prr.addCurReceiver(r);    app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);    mService.updateLruProcessLocked(app, false, null);    mService.enqueueOomAdjTargetLocked(app);    mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);    r.intent.setComponent(r.curComponent);    boolean started = false;    try {        mService.notifyPackageUse(r.intent.getComponent().getPackageName(),                                  PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);        // 通知进程启动 receiver 来处理广播        thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,                mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),                r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,                app.mState.getReportedProcState());        started = true;    } finally {        if (!started) {            // ...        }    }}

现在 AMS 通知 receiver 所在的进程来处理广播

// ActivityThread.javaprivate class ApplicationThread extends IApplicationThread.Stub {    private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";    public final void scheduleReceiver(Intent intent, ActivityInfo info,            CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,            boolean sync, int sendingUser, int processState) {        updateProcessState(processState, false);        // 广播数据包装成 ReceiverData        ReceiverData r = new ReceiverData(intent, resultCode, data, extras,                sync, false, mAppThread.asBinder(), sendingUser);        r.info = info;        r.compatInfo = compatInfo;        sendMessage(H.RECEIVER, r);    }

最终调用 handleReceiver() 处理广播数据

private void handleReceiver(ReceiverData data) {    unscheduleGCIdler();    String component = data.intent.getComponent().getClassName();    LoadedApk packageInfo = getPackageInfoNoCheck(            data.info.applicationInfo, data.compatInfo);    IActivityManager mgr = ActivityManager.getService();    Application app;    BroadcastReceiver receiver;    ContextImpl context;    try {        // 1. 创建 Application 对象,并调用 Application#onCreate()        app = packageInfo.makeApplication(false, mInstrumentation);        // ...        // 2. 创建 BroadcastReceiver 对象        receiver = packageInfo.getAppFactory()                .instantiateReceiver(cl, data.info.name, data.intent);    } catch (Exception e) {        // ...    }    try {        sCurrentBroadcastIntent.set(data.intent);        receiver.setPendingResult(data);        // 3. 执行 BroadcastReceiver#onReceive()        receiver.onReceive(context.getReceiverRestrictedContext(),                data.intent);    } catch (Exception e) {        // ...    } finally {        sCurrentBroadcastIntent.set(null);    }    // 4. 返回广播的处理结果给 AMS    if (receiver.getPendingResult() != null) {        data.finish();    }}

这里的过程很清晰明了吧,直接看最后一步,把广播的处理结果反馈给 AMS

// BroadcastReceiver.javapublic final void finish() {    if (mType == TYPE_COMPONENT) {        final IActivityManager mgr = ActivityManager.getService();        if (QueuedWork.hasPendingWork()) {            // ...        } else {            sendFinished(mgr);        }    } else if (mOrderedHint &amp;&amp; mType != TYPE_UNREGISTERED) {        // ...    }}public void sendFinished(IActivityManager am) {    synchronized (this) {        if (mFinished) {            throw new IllegalStateException("Broadcast already finished");        }        mFinished = true;        try {            if (mResultExtras != null) {                mResultExtras.setAllowFds(false);            }            if (mOrderedHint) {                // 有序广播的反馈                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,                        mAbortBroadcast, mFlags);            } else {                // 非有序广播的费奎                am.finishReceiver(mToken, 0, null, null, false, mFlags);            }        } catch (RemoteException ex) {        }    }}

现在看下 AMS 如何处理这个反馈的结果

// ActivityManagerService.javapublic void finishReceiver(IBinder who, int resultCode, String resultData,        Bundle resultExtras, boolean resultAbort, int flags) {    // ...    final long origId = Binder.clearCallingIdentity();    try {        boolean doNext = false;        BroadcastRecord r;        BroadcastQueue queue;        synchronized(this) {            if (isOnOffloadQueue(flags)) {                queue = mOffloadBroadcastQueue;            } else {                queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0                        ? mFgBroadcastQueue : mBgBroadcastQueue;            }            // 1. 匹配进程正在处理的广播            r = queue.getMatchingOrderedReceiver(who);            // 2. 完成当前 receiver 广播的处理流程            if (r != null) {                doNext = r.queue.finishReceiverLocked(r, resultCode,                    resultData, resultExtras, resultAbort, true);            }            // 3. 发送广播给下一个 receiver,或者发送下一个广播            if (doNext) {                r.queue.processNextBroadcastLocked( false,  true);            }            // updateOomAdjLocked() will be done here            trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);        }    } finally {        Binder.restoreCallingIdentity(origId);    }}

看到了,只有当前 receiver 处理完广播,才会发送广播给下一个 receiver,这就是“串行”广播的本质。

广播 ANR

最后,来探讨一个广播 ANR 的原理,本来我以为很简单的,就是发送一个超时消息嘛。但是当我细看的时候,我发现这个 ANR 设计的很巧妙,我觉得我们可以学习下,因此这里单独拿出来分析。

这里,我得提醒大家一点,只有“串行”广播才会发生 ANR,因为它要等待 receiver 的处理结果。

根据前面分析,“串行”广播发送给 receiver 前,会发送一个超时消息,如下

// BroadcastQueue.javaif (! mPendingBroadcastTimeoutMessage) {    long timeoutTime = r.receiverTime + mConstants.TIMEOUT;    setBroadcastTimeoutLocked(timeoutTime);}

当这个消息被执行的时候,会调用如下代码

// BroadcastQueue.javafinal void broadcastTimeoutLocked(boolean fromMsg) {    if (fromMsg) {        mPendingBroadcastTimeoutMessage = false;    }    if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {        return;    }    long now = SystemClock.uptimeMillis();    // 获取当前正在处理的广播    BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();    if (fromMsg) {        // 系统还没有就绪        if (!mService.mProcessesReady) {            return;        }        // 广播超时被豁免        if (r.timeoutExempt) {            if (DEBUG_BROADCAST) {                Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "                        + r.intent.getAction());            }            return;        }        // 1. 广播没有超时        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;        if (timeoutTime > now) {            // 发送下一个超时消息            setBroadcastTimeoutLocked(timeoutTime);            return;        }    }    if (r.state == BroadcastRecord.WAITING_SERVICES) {        // ...        return;    }    // 2. 走到这里,表示广播超时,触发 ANR    final boolean debugging = (r.curApp != null && r.curApp.isDebugging());    r.receiverTime = now;    if (!debugging) {        r.anrCount++;    }    ProcessRecord app = null;    String anrMessage = null;    Object curReceiver;    if (r.nextReceiver > 0) {        curReceiver = r.receivers.get(r.nextReceiver-1);        r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;    } else {        curReceiver = r.curReceiver;    }    logBroadcastReceiverDiscardLocked(r);    // 获取 receiver 进程    if (curReceiver != null && curReceiver instanceof BroadcastFilter) {        BroadcastFilter bf = (BroadcastFilter)curReceiver;        if (bf.receiverList.pid != 0                && bf.receiverList.pid != ActivityManagerService.MY_PID) {            synchronized (mService.mPidsSelfLocked) {                app = mService.mPidsSelfLocked.get(                        bf.receiverList.pid);            }        }    } else {        app = r.curApp;    }    if (app != null) {        anrMessage = "Broadcast of " + r.intent.toString();    }    if (mPendingBroadcast == r) {        mPendingBroadcast = null;    }    // 强制结束当前广播的发送流程    finishReceiverLocked(r, r.resultCode, r.resultData,            r.resultExtras, r.resultAbort, false);    // 调度下一次的广播发送    scheduleBroadcastsLocked();    // app 不处于 debug 模式,引发 ANR    if (!debugging && anrMessage != null) {        mService.mAnrHelper.appNotResponding(app, anrMessage);    }}

第2步,引发 ANR ,很简单,就是因为整个发送与反馈过程超时了。

而第1步,就是处理不超时的情况。这里大家是不是很疑惑,移除超时消息不是在接收到广播反馈后进行的吗? 我可以负责地告诉你,并不是!那这里第1步怎么理解呢?

首先这个超时消息一定触发,但是触发这个超时消息,并不代表一定会引发 ANR。

假如当前 receiver 的广播处理流程,在超时时间之前就完成了,那么 AMS 会调度广播发送给下一个 receiver。

于是,针对下一个 receiver ,会更新 r.receiverTime,那么第一步此时计算出来的 timeoutTime 是下一个 receiver 的广播超时时间,很显然是大于 now 的,于是就不会走第2步的 ANR 流程。

最后利用这个 timeoutTime,为下一个 receiver 再发送一个超时消息,简直是完美!

至于为何不在广播反馈的时候,移除这个超时消息,我心中有一点小小的想法,但是也不能确定是不是这个原因,才这样设计的。不过,对我来说,这一招,我算是学会了。

感谢各位的阅读,以上就是“ActivityManagerService广播并行发送与串行发送怎么实现”的内容了,经过本文的学习后,相信大家对ActivityManagerService广播并行发送与串行发送怎么实现这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: ActivityManagerService广播并行发送与串行发送怎么实现

本文链接: https://www.lsjlt.com/news/350521.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • ActivityManagerService广播并行发送与串行发送怎么实现
    这篇文章主要讲解了“ActivityManagerService广播并行发送与串行发送怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ActivityManagerService广播并...
    99+
    2023-07-05
  • ActivityManagerService广播并行发送与串行发送示例解析
    目录"并行"广播的发送“串行”广播的发送广播发送给正在启动的进程广播 ANR结束"并行"广播的发送 本文以 Acti...
    99+
    2023-03-02
    ActivityManagerService广播并行发送 ActivityManagerService广播串行发送
  • ActivityManagerService广播怎么注册与发送
    今天小编给大家分享一下ActivityManagerService广播怎么注册与发送的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一...
    99+
    2023-07-05
  • ActivityManagerService广播注册与发送示例解析
    目录引言注册广播接收器发送广播结束引言 最近,帮同事解决了两个问题,一个问题是 app 接收开机广播的速度太慢,另一个问题是app有时无法接收到广播。同事不知道如何解决这个问题,是...
    99+
    2023-03-02
    ActivityManagerService广播注册发送 ActivityManagerService 广播
  • linux怎么发送广播消息
    在Linux系统中,可以使用`wall`命令来发送广播消息。`wall`命令用于向所有登录到系统的用户发送消息,格式如下:```wa...
    99+
    2023-09-04
    linux
  • 有序广播怎么在Android应用中进行发送
    有序广播怎么在Android应用中进行发送?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Android系统提供了两种广播类型,一种是有序广播,一种是有序广播。(1)无序广播是完...
    99+
    2023-05-31
    android roi
  • 在android怎么发送广播消息
    在Android中,可以通过以下步骤来发送广播消息:1. 创建一个`Intent`对象,用于描述广播消息的内容和目标。```java...
    99+
    2023-09-04
    android
  • Android怎么使用广播发送消息
    本文小编为大家详细介绍“Android怎么使用广播发送消息”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android怎么使用广播发送消息”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。具体效果如下activit...
    99+
    2023-06-30
  • 怎么在Android中利用Intent发送广播消息
    这篇文章给大家介绍怎么在Android中利用Intent发送广播消息,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Android Intent发送广播消息Intent的另一种用途是发送广播消息,应用程序和Android...
    99+
    2023-05-31
    android intent roi
  • Redis怎么实现验证码发送并限制每日发送次数
    这篇文章主要讲解了“Redis怎么实现验证码发送并限制每日发送次数”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Redis怎么实现验证码发送并限制每日发送次数”吧!1、功能输入手机号,点击发...
    99+
    2023-06-30
  • Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战
    目录本篇内容:功能场景点: ① pom引入核心依赖 ② yml加上配置项③ 创建socket配置加载类 MySocketConfig.java④创建消息...
    99+
    2022-11-13
    Springboot Socket单点发送 Springboot整合Socket Springboot Socket广播群发
  • python串口数据怎么采集并发送
    要在Python中采集和发送串口数据,你需要使用一个串口通信库,比如pySerial。下面是一个简单的示例代码,展示了如何使用pyS...
    99+
    2023-10-10
    python
  • PHP实现发送邮件功能代码|PHP怎么实现QQ邮件发送|Php发送邮件代码
    最近学习PHP的过程中发现了一个很实用的功能那就是发送QQ邮件,因为这个功能很常用我也是研究了半天找到一个很好的demo感兴趣的可以自己下载学习一下这个Php发送邮件代码真的很实用而且很好用 使用方法: 上传整体压缩包到服务器解压 修改se...
    99+
    2023-09-16
    php 服务器 apache
  • Python怎么实现直播弹幕自动发送功能
    这篇文章主要讲解了“Python怎么实现直播弹幕自动发送功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python怎么实现直播弹幕自动发送功能”吧!前言先打开一个直播间按F12打开开发者...
    99+
    2023-06-29
  • Java怎么实现短信发送
    本篇内容主要讲解“Java怎么实现短信发送”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java怎么实现短信发送”吧!1. 引入相关maven依赖<dependency><gr...
    99+
    2023-06-22
  • SpringBoot怎么实现短信发送
    这篇“SpringBoot怎么实现短信发送”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“SpringBoot怎么实现短信发送...
    99+
    2023-06-29
  • android怎么实现发送验证码
    在Android中,可以通过使用短信管理器(SmsManager)来发送验证码。首先,你需要在AndroidManifest.xml...
    99+
    2023-09-04
    android
  • js怎么实现异步串行与异步并行
    本篇内容主要讲解“js怎么实现异步串行与异步并行”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“js怎么实现异步串行与异步并行”吧!js异步处理方案,js的异步串行与异步并行一、什么是串行,并行,...
    99+
    2023-07-05
  • Python Http发送请求怎么实现
    本篇内容介绍了“Python Http发送请求怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!浅析requests在不借助其...
    99+
    2023-07-02
  • java socket发送数据怎么实现
    要使用Java Socket发送数据,可以按照以下步骤进行实现:1. 创建一个Socket对象,并指定要连接的服务器主机名和端口号。...
    99+
    2023-09-23
    java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作