Android广播机制简单分析

重点分析流程,而不陷于代码,可以照着流程再细看。

一、广播注册

//客户端注册广播的例子
mBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.broadcast.test");
registerReceiver(mBroadcastReceiver, intentFilter);

registerReceiver其实是ContexImpl来调用的,走过几个类似重载的registerReceiver方法,最终来到registerReceiverInternal方法。在registerReceiverInternal里构建出ReceiverDispatcher,ReceiverDispatcher具有跨进程的binder内部类IIntentReceiver,然后交给ActivityManagerService的registerReceiver方法去完成注册。ActivityManagerService用 mRegisteredReceivers存储注册的动态广播,

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

key为注册的receiver,value为ReceiverList,ReceiverList为ArrayList<BroadcastFilter>列表,而BroadcastFilter extends IntentFilter ,当发送广播后找匹配的receiver时,通过IntentFilter匹配,找动态注册的BroadcastFilter。 下面几句表示的BroadcastReceiver存储的方式:

ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);
rl.add(bf);
mReceiverResolver.addFilter(bf);

上面注意最后一句,mReceiverResolver的作用是什么呢?见源码,下一节分析。

/**
     * Resolver for broadcast intents to registered receivers.
     * Holds BroadcastFilter (subclass of IntentFilter).
     */
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
= new IntentResolver<BroadcastFilter, BroadcastFilter>() {

二、发送广播

//发送广播的例子
Intent intent = new Intent();
intent.setAction("android.broadcast.test");
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//sendBroadcast(intent);
sendOrderedBroadcast(intent, null);

客户端的sendBroadcast最终会走到ActivityManagerService的broadcastIntentLocked,前面很多代码是权限之类的检查,忽略掉,重点分析怎么发送。因为存在动态广播和静态广播,所以检查sendBroadcast的Intent是不是只有动态广播receiver可以接收,通过下面这句,如果不只是动态广播receiver能接收,那就要也发送那些静态注册的receiver。registeredReceivers是动态广播接收器列表 ,receivers是静态广播接收器列表。

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

还记得在广播注册最后的mReceiverResolver吗?在这里派上用场,registeredReceivers通过下面这句得到。

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

这样就得到了符合能接收Intent的所有Receiver。
下面要构建一个BroadcastRecord对象,并入队发送出去。首先的问题是入哪个队列,有ActivityManagerService前台和后台两个队列,如果发送的Intent有Intent.FLAG_RECEIVER_FOREGROUND就选前台队列,否则选后台队列。
前台和后台队列又分别含有两个列表mParallelBroadcasts和mOrderedBroadcasts对应无序广播和有序广播。
最后发送时,调用

//无序广播
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
//有序广播
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();

三、广播的处理

scheduleBroadcastsLocked只是通过Handler发送一个消息BROADCAST_INTENT_MSG,Handler收到消息后调用processNextBroadcast,这里如果是无序广播,一次处理,走到deliverToRegisteredReceiverLocked,然后调用LoadedApk中ReceiverDispatcher的performReceive,在里面构造一个叫Args的Runnable,然后ActivityThread中的H这个Handler去post执行。
这样Runnable的run()方法执行,走到了我们熟悉的onReceive回调。

ClassLoader cl =  mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);

四、其他东西

以上简单分析了广播的整个流程,中间很多细节的东西还需要精读源码分析,比如有序广播怎么处理的,静态广播怎么处理的。
下面列一些结论性的东西:

// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

1.默认动态注册的广播ANR时间为60S,说明默认动态注册的广播为后台广播。
2.无序广播不会ANR,有序广播会ANR。但是广播回调onReceive如果在主线程耗时,会出现ANR,此ANR非广播ANR,而是Activity的ANR。
3.ActivityManagerService中有两个BroadcastQueue,分别是mFgBroadcastQueue和mBgBroadcastQueue,对应前台广播和后台广播,而每个BroadcastQueue中又含有两个ArrayList<BroadcastRecord>,分别为mParallelBroadcasts和mOrderedBroadcasts,对应并行广播和有序广播。
4.ANR简单流程如下,重点在函数processNextBroadcast:取得下一个有序广播,Handler发送延迟ANR msg,如果没有TIMEOUT,Handler remove ANR msg,如果TIMEOUT,Handler执行AppNotResponding这个Runnable。

//前台广播mTimeoutPeriod为10s,后台广播为60s,通过Handler发送BROADCAST_TIMEOUT_MSG
if (! mPendingBroadcastTimeoutMessage) {
       long timeoutTime = r.receiverTime + mTimeoutPeriod;
       setBroadcastTimeoutLocked(timeoutTime);
}
//如果没有timeout,将之前的BROADCAST_TIMEOUT_MSG取消掉
  final void cancelBroadcastTimeoutLocked() {
        if (mPendingBroadcastTimeoutMessage) {
            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
            mPendingBroadcastTimeoutMessage = false;
        }
    }
/*如果到了timeout时间,还没有取消BROADCAST_TIMEOUT_MSG,就会执行这个,
调用到broadcastTimeoutLocked,最终通过Handler来post AppNotResponding这个Runnable*/
 @Override
  public void handleMessage(Message msg) {
          switch (msg.what) {
             case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                          broadcastTimeoutLocked(true);
              }
           } break;

当然还有个地方也会发生ANR,和Receiver的个数有关系,而且不是通过Handler来发生的,当发生Timeout时,直接调用broadcastTimeoutLocked

//在processNextBroadcast方法里
 if (mService.mProcessesReady && r.dispatchTime > 0) {
        long now = SystemClock.uptimeMillis();
        if ((numReceivers > 0) &&(now > r.dispatchTime + 
          (2*mTimeoutPeriod*numReceivers))) {
               broadcastTimeoutLocked(false); // forcibly finish this broadcast
               forceReceive = true;
               r.state = BroadcastRecord.IDLE;
  }

5.设置 intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);这个flag后,广播不会发送给已经停止的应用,系统默认是不让我们的广播发送给已经停止的应用的。

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