Android广播的注册和发送原理(基于Android S)

Android广播机制采用类似于“发布-订阅”者的设计模式,基于Binder机制,实现了一对多的通信。

从整体的角度看,广播的使用主要设计两个“动作” ,“注册广播”和“发送广播”,以及三个“角色”,发送方,接收方和中间人。为什么需要中间人,因为有了中间人的存在,就可以减少发送方和接收方的工作而不用去关心具体的实现细节。三者关系可参考下图:

AMS就是上面提到的中介,也是最重要的角色。Receiver向AMS注册自己感兴趣的广播,AMS则必须将所有必要的信息记录下来,比如Receiver的一些必要信息以及注册的广播的信息,统统都以一定的方式保存起来。当Sender发送广播后,AMS需要根据广播的信息从数据容器中找到注册该广播的Receiver的相关信息。此外AMS还要对很多特殊情况进行处理,和广播是否被及时处理,如果超时会产生anr,这些工作都是由AMS去做的。

一、广播的注册

如上文所说,广播的注册最终是由AMS去完成的,接收方只需要准备好一些必要的信息,如接收者和感兴趣的广播等,并将这些信息提供给AMS,最终由AMS去维护这些信息。

这里列出AMS从Receiver接收到的参数或者说是信息:

IApplicationThread callerReceiver所在的主线程的ApplicationThread
String callerPackageReceiver所在的包名
String callerFeatureIdapp访问受保护数据的属性标签
String receiverIdReceiver的id
IIntentReceiver receiverInnerReceiver对象,ReceiverDispatcher的内部类
IntentFilter filterIntent过滤器,用来匹配Intent的values
String permission广播权限
int userIdmUser.getIdentifier,ContextImpl持有mUser
int flags用来判断对InstantApp的可见性

接下来,看下AMS怎么进行注册的:

这里只关注核心逻辑,很多根据部分参数进行的特殊情况处理在这里不进行分析,粘性广播也不分析。

/*AMS*/

public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
    String callerFeatureId, String receiverId, IIntentReceiver receiver,
            IntentFilter filter, String permission, int userId, int flags) {
    
    synchronized (this) {

        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) {
            //rl为空,说明当前调用端(客户端)还没有注册过广播
            rl = new ReceiverList(this, callerApp, callingPid, callingUid,
            userId, receiver);
            if (rl.app != null) {
                //ReceiverList持有ProcessRecord对象app,app持有ProcessReceiverRecord对象mReceivers,mReceivers内部维护一个
                //里边存放着从当前应用注册的所有IIntentReceiver
                final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
                //当前应用可注册的广播数目超过了阈值,则抛出异常
                if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                    throw new IllegalStateException("Too many receivers, total of "
                    + totalReceiversForApp + ", registered for pid: "
                    + rl.pid + ", callerPackage: " + callerPackage);
                }
                //这个关系链是这样的:ReceiverList  ---> ProcessRecord ---> ProcessReceiverRecord --->  ArraySet<Rec
                rl.app.mReceivers.addReceiver(rl);
            } else {
                try {
                    receiver.asBinder().linkToDeath(rl, 0);
                } catch (RemoteException e) {
                    return sticky;
                }
                rl.linkedToDeath = true;
            }
            //更新mRegisteredReceivers
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        } else if (...) {
        ...
        //这里省略多个else if
        }

        ...

        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
        receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);
        if (rl.containsFilter(filter)) {
            Slog.w(TAG, "Receiver with filter " + filter
            + " already registered for pid " + rl.pid
            + ", callerPackage is " + callerPackage);
        } else {
        rl.add(bf);
        if (!bf.debugCheck()) {
            Slog.w(TAG, "==> For Dynamic broadcast");
        }
        mReceiverResolver.addFilter(bf);
}

这里要关注两个变量,mRegisteredReceivers和mReceiverResolver:

/**
 * Keeps track of all IIntentReceivers that have been registered for broadcasts.
 * Hash keys are the receiver IBinder, hash value is a ReceiverList.
 */
@GuardedBy("this")
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

可以看出,mRegisterReceivers是AMS持有的一个HashMap,key是IIntentReceiver,value是ReceiverList,ReceiverList继承自ArrayList并封装了一些关于Receiver的属性。

/**
 * Resolver for broadcast intents to registered receivers.
 * Holds BroadcastFilter (subclass of IntentFilter).
 */
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
        = new IntentResolver<BroadcastFilter, BroadcastFilter>() {
    @Override
    protected boolean allowFilterResult(
            BroadcastFilter filter, List<BroadcastFilter> dest) {
        IBinder target = filter.receiverList.receiver.asBinder();
        for (int i = dest.size() - 1; i >= 0; i--) {
            if (dest.get(i).receiverList.receiver.asBinder() == target) {
                return false;
            }
        }
        return true;
    }
    @Override
    protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
        if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
                || userId == filter.owningUserId) {
            return super.newResult(filter, match, userId);
        }
        return null;
    }
    @Override
    protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {
        return input;
    }
    @Override
    protected BroadcastFilter[] newArray(int size) {
        return new BroadcastFilter[size];
    }
    @Override
    protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
        return packageName.equals(filter.packageName);
    }
};

IntentResolver是一个抽象类,是一个对Intent的解析器,它持有一个ArraySet用来存放filters,mReceiverResolver.addFilter(bf); 就是将bf放到了mReceiverResolver持有的ArraySet中。

二、广播的发送

同样的,广播的发送的重头戏也是在AMS,由于AMS的方法动辄近千行,这里还是把AMS收到的参数都列出来,然后看看AMS要做什么。

下面是sendBroadcast(Intent intent),也就是说普通广播,传给AMS的参数:

IApplicationThread caller
发送方主线程的ApplicationThread
String callingFeatureId
app访问受保护数据的属性标签
Intent intent
用于发送广播的intent
String resolvedType
intent的MIME数据类型(不用太关注)
IIntentReceiver resultTo
null,IntentReceiver的接口
int resultCode
Activity.RESULT_OK(-1)
String resultData
null
Bundle resultExtras
null
String[] requiredPermissions
null
String[] excludedPermissions
null
int appOp
AppOpsManager.OP_NONE()
Bundle bOptions
null,携带的一些数据
boolean serialized
fasle,是否是有序广播
boolean sticky
false,是否是粘性广播
int userId
getUserId()

下面开始分析AMS的代码,同样的,跳过粘性广播。

...

List<BroadcastFilter> registeredReceivers = null;

...

registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);

这里可以注意到mReceiverResolver这个变量,在广播注册时就碰见过。当时是把BroadcastFilter放到了mReceiverResolver持有的ArraySet中,我们现在大胆猜测现在要把BroadcastFilter取出来,看看queryIntent方法验证下!

public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
            int userId) {

    ...

    ArrayList<R> finalList = new ArrayList<R>();

    ...
    
    FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
    if (firstTypeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
            scheme, firstTypeCut, finalList, userId);
    }
    if (secondTypeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                scheme, secondTypeCut, finalList, userId);
    }
    if (thirdTypeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
            scheme, thirdTypeCut, finalList, userId);
    }
    if (schemeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
            scheme, schemeCut, finalList, userId);
    }
    filterResults(finalList);
    sortResults(finalList);
    if (debug) {
        Slog.v(TAG, "Final result list:");
        for (int i=0; i<finalList.size(); i++) {
            Slog.v(TAG, "  " + finalList.get(i));
        }
    }
    return finalList;
}

这里根据intent拿到匹配到的BroadcastFilter的List,接下来就开始处理这些BroadcastFilter。

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.
    if (isCallerSystem) {
        checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                isProtectedBroadcast, registeredReceivers);
    }
    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    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);
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
    final boolean replaced = replacePending
            && (queue.replaceParallelBroadcastLocked(r) != null);
    // Note: We assume resultTo is null for non-ordered broadcasts.
    if (!replaced) {
        queue.enqueueParallelBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }
    registeredReceivers = null;
    NR = 0;
}

大家自己看吧,这里再看下enqueueParallelBroadcastLocked(r)和scheduleBroadcastsLocked()

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
    mParallelBroadcasts.add(r);
    enqueueBroadcastHelper(r);
}


public void scheduleBroadcastsLocked() {
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
            + mQueueName + "]: current="
            + mBroadcastsScheduled);
    if (mBroadcastsScheduled) {
        return;
    }
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}
final BroadcastHandler mHandler;
private final class BroadcastHandler extends Handler {
    public BroadcastHandler(Looper looper) {
        super(looper, null, true);
    }
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_INTENT_MSG: {
                if (DEBUG_BROADCAST) Slog.v(
                        TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
                        + mQueueName + "]");
                processNextBroadcast(true);
            } break;
            case BROADCAST_TIMEOUT_MSG: {
                synchronized (mService) {
                    broadcastTimeoutLocked(true);
                }
            } break;
        }
    }
}
private void processNextBroadcast(boolean fromMsg) {
    synchronized (mService) {
        processNextBroadcastLocked(fromMsg, false);
    }
}
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    ...
    // First, deliver any non-serialized broadcasts right away.
    while (mParallelBroadcasts.size() > 0) {
        r = mParallelBroadcasts.remove(0);
        r.dispatchTime = SystemClock.uptimeMillis();
        r.dispatchClockTime = System.currentTimeMillis();
        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
                System.identityHashCode(r));
            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
            createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
            System.identityHashCode(r));
        }
        final int N = r.receivers.size();
        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
            + mQueueName + "] " + r);
        for (int i=0; i<N; i++) {
            Object target = r.receivers.get(i);
            if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                "Delivering non-ordered on [" + mQueueName + "] to registered "
                + target + ": " + r);
            deliverToRegisteredReceiverLocked(r,
                (BroadcastFilter) target, false, i);
        }
        addBroadcastToHistoryLocked(r);
        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast [" + mQueueName + "] " + r);
    }

    ...


}

到这吧,如果有观众看到这,自己往下看吧。 

三、总结

本文试图从源码分析广播的注册和发送的原理,奈何源码实在有点庞大,讲的太细根本不可能。我自己也意识到,可能自己阅读源码的方式有问题。不能为了看源码而去看源码,而应该带着自己的问题去阅读源码,只要阅读之后能回答自己的问题就是值得的,就是有收获的。以后就这么看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安卓夜问

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值