Broadcast(四)有序广播

接上一篇,我们看看这2个方法做了什么

queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();

// mOrderedBroadcasts增加一条广播
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
    mOrderedBroadcasts.add(r);
    r.enqueueClockTime = System.currentTimeMillis();
}

第二篇解析了scheduleBroadcastsLocked最终调用processNextBroadcast,step1也解析过,本文接着看step234

final void processNextBroadcast(boolean fromMsg) {
    //全程保持AMS锁,其他线程(app进程binder通信)都需要等待此方法处理完毕
    synchronized(mService) {
        //step1:处理全部无序广播,遍历动态注册的接收者并通知
        //step2:获取第一条有序广播
        //step3:获取第一个接收者,设置超时
        //step4:如果是动态注册的接收者,通知并return
        //step5:如果是静态注册的接收者,校验权限
        //step6:通知接收者
    }
}

step2:获取第一条有序广播

do {
    // mOrderedBroadcasts如果没值则return
    // 这里再重复说下,sendBroadcast有静态注册的接收者或者sendOrderedBroadcast
    // mOrderedBroadcasts会增加一条记录,不会为空
    if (mOrderedBroadcasts.size() == 0) {
        // No more broadcasts pending, so all done!
        mService.scheduleAppGcsLocked();
        if (looped) {
            // If we had finished the last ordered broadcast, then
            // make sure all processes have correct oom and sched
            // adjustments.
            mService.updateOomAdjLocked();
        }
        return;
    }
    r = mOrderedBroadcasts.get(0);
    boolean forceReceive = false;
    ...
} while (r == null);

step3:获取第一个接收者,设置超时

// Get the next receiver...
int recIdx = r.nextReceiver++;

// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
    r.dispatchTime = r.receiverTime;
    r.dispatchClockTime = System.currentTimeMillis();
}
if (! mPendingBroadcastTimeoutMessage) {
    long timeoutTime = r.receiverTime + mTimeoutPeriod;
    setBroadcastTimeoutLocked(timeoutTime);
}
...
final Object nextReceiver = r.receivers.get(recIdx);

超时时间就是mTimeoutPeriod

BroadcastQueue(ActivityManagerService service, Handler handler,
               String name, long timeoutPeriod, boolean allowDelayBehindServices) {
    mService = service;
    mHandler = new BroadcastHandler(handler.getLooper());
    mQueueName = name;
    mTimeoutPeriod = timeoutPeriod;
    mDelayBehindServices = allowDelayBehindServices;
}

我们看AMS里

static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
        "foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
        "background", BROADCAST_BG_TIMEOUT, true);

所以是前台广播10s超时,后台广播(默认是后台广播)60s超时
setBroadcastTimeoutLocked设置超时,关于超时和ANR见后续文章[Broadcast(五)ANR]
step4:如果是动态注册的接收者,通知并return

 if (nextReceiver instanceof BroadcastFilter) {
    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
    // 通知接收者,见[Broadcast(二)registerReceiver时sticky广播]
    deliverToRegisteredReceiverLocked(r, filter, r.ordered);
    if (r.receiver == null || !r.ordered) {
        // The receiver has already finished, so schedule to
        // process the next one.
        r.state = BroadcastRecord.IDLE;
        scheduleBroadcastsLocked();
    } else {
        if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
            scheduleTempWhitelistLocked(filter.owningUid,
                    brOptions.getTemporaryAppWhitelistDuration(), r);
        }
    }
    return;
}

step5:如果是静态注册的接收者,校验权限

ResolveInfo info =
        (ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
        info.activityInfo.applicationInfo.packageName,
        info.activityInfo.name);

boolean skip = false;
int perm = mService.checkComponentPermission(info.activityInfo.permission,
        r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
        info.activityInfo.exported);
if (perm != PackageManager.PERMISSION_GRANTED) {
    skip = true;
} else if (info.activityInfo.permission != null) {
    final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
    if (opCode != AppOpsManager.OP_NONE
            && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
            r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
        skip = true;
    }
}
if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
        r.requiredPermissions != null && r.requiredPermissions.length > 0) {
    for (int i = 0; i < r.requiredPermissions.length; i++) {
        String requiredPermission = r.requiredPermissions[i];
        try {
            perm = AppGlobals.getPackageManager().
                    checkPermission(requiredPermission,
                            info.activityInfo.applicationInfo.packageName,
                            UserHandle
                                    .getUserId(info.activityInfo.applicationInfo.uid));
        } catch (RemoteException e) {
            perm = PackageManager.PERMISSION_DENIED;
        }
        if (perm != PackageManager.PERMISSION_GRANTED) {
            skip = true;
            break;
        }
        int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
        if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                && mService.mAppOpsService.noteOperation(appOp,
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
                != AppOpsManager.MODE_ALLOWED) {
            skip = true;
            break;
        }
    }
}
if (!skip && r.appOp != AppOpsManager.OP_NONE
        && mService.mAppOpsService.noteOperation(r.appOp,
        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
        != AppOpsManager.MODE_ALLOWED) {
    skip = true;
}
if (!skip) {
    skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
            r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
boolean isSingleton = false;
try {
    isSingleton = mService.isSingleton(info.activityInfo.processName,
            info.activityInfo.applicationInfo,
            info.activityInfo.name, info.activityInfo.flags);
} catch (SecurityException e) {
    skip = true;
}
if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
    if (ActivityManager.checkUidPermission(
            android.Manifest.permission.INTERACT_ACROSS_USERS,
            info.activityInfo.applicationInfo.uid)
            != PackageManager.PERMISSION_GRANTED) {
        skip = true;
    }
}
if (r.curApp != null && r.curApp.crashing) {
    // If the target process is crashing, just skip it.
    skip = true;
}
if (!skip) {
    boolean isAvailable = false;
    try {
        isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
                info.activityInfo.packageName,
                UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
    } catch (Exception e) {
        // all such failures mean we skip this receiver
    }
    if (!isAvailable) {
        skip = true;
    }
}

if (skip) {
    r.receiver = null;
    r.curFilter = null;
    r.state = BroadcastRecord.IDLE;
    scheduleBroadcastsLocked();
    return;
}
...
// Broadcast is being executed, its package can't be stopped.
try {
    AppGlobals.getPackageManager().setPackageStoppedState(
            r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
}

step6:通知接收者

// Is this receiver's application already running?
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
        info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) {
    try {
        app.addPackage(info.activityInfo.packageName,
                info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
        processCurBroadcastLocked(r, app);
        return;
    } catch (RemoteException e) {
    } catch (RuntimeException e) {
        r.state = BroadcastRecord.IDLE;
        return;
    }
    ...

processCurBroadcastLocked

private final void processCurBroadcastLocked(BroadcastRecord r,
                                             ProcessRecord app) throws RemoteException {
    if (app.thread == null) {
        throw new RemoteException();
    }
    r.receiver = app.thread.asBinder();
    r.curApp = app;
    app.curReceiver = r;
    app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
    mService.updateLruProcessLocked(app, false, null);
    mService.updateOomAdjLocked();

    // Tell the application to launch this receiver.
    r.intent.setComponent(r.curComponent);

    boolean started = false;
    try {
        mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
        app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                app.repProcState);
        started = true;
    } finally {
        if (!started) {
            r.receiver = null;
            r.curApp = null;
            app.curReceiver = null;
        }
    }
}

至此只处理了一个receiver,其他receiver呢,我们看下时序图

app receiver处理完毕会通知system,然后接着执行processNextBroadcast,每次执行时r.nextReceiver++

int recIdx = r.nextReceiver++;
...
final Object nextReceiver = r.receivers.get(recIdx);
...
ResolveInfo info = (ResolveInfo)nextReceiver;
...
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
    info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) {
    try {
        app.addPackage(info.activityInfo.packageName,
                info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
        processCurBroadcastLocked(r, app);
...

到最后receivers.get(recIdx)就取不到值了

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

推荐阅读更多精彩内容