Android廣播機(jī)制簡單分析

重點(diǎn)分析流程西雀,而不陷于代碼,可以照著流程再細(xì)看。

一歉摧、廣播注冊

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

registerReceiver其實(shí)是ContexImpl來調(diào)用的蒋搜,走過幾個類似重載的registerReceiver方法,最終來到registerReceiverInternal方法判莉。在registerReceiverInternal里構(gòu)建出ReceiverDispatcher,ReceiverDispatcher具有跨進(jìn)程的binder內(nèi)部類IIntentReceiver育谬,然后交給ActivityManagerService的registerReceiver方法去完成注冊券盅。ActivityManagerService用 mRegisteredReceivers存儲注冊的動態(tài)廣播,

/**
* 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 咖刃,當(dāng)發(fā)送廣播后找匹配的receiver時泳炉,通過IntentFilter匹配,找動態(tài)注冊的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的作用是什么呢?見源碼,下一節(jié)分析枫浙。

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

二刨肃、發(fā)送廣播

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

客戶端的sendBroadcast最終會走到ActivityManagerService的broadcastIntentLocked,前面很多代碼是權(quán)限之類的檢查箩帚,忽略掉真友,重點(diǎn)分析怎么發(fā)送。因?yàn)榇嬖趧討B(tài)廣播和靜態(tài)廣播紧帕,所以檢查sendBroadcast的Intent是不是只有動態(tài)廣播receiver可以接收盔然,通過下面這句,如果不只是動態(tài)廣播receiver能接收,那就要也發(fā)送那些靜態(tài)注冊的receiver愈案。registeredReceivers是動態(tài)廣播接收器列表 挺尾,receivers是靜態(tài)廣播接收器列表。

 // 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崇众。
下面要構(gòu)建一個BroadcastRecord對象掂僵,并入隊(duì)發(fā)送出去。首先的問題是入哪個隊(duì)列顷歌,有ActivityManagerService前臺和后臺兩個隊(duì)列锰蓬,如果發(fā)送的Intent有Intent.FLAG_RECEIVER_FOREGROUND就選前臺隊(duì)列,否則選后臺隊(duì)列眯漩。
前臺和后臺隊(duì)列又分別含有兩個列表mParallelBroadcasts和mOrderedBroadcasts對應(yīng)無序廣播和有序廣播芹扭。
最后發(fā)送時,調(diào)用

//無序廣播
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
//有序廣播
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();

三赦抖、廣播的處理

scheduleBroadcastsLocked只是通過Handler發(fā)送一個消息BROADCAST_INTENT_MSG舱卡,Handler收到消息后調(diào)用processNextBroadcast,這里如果是無序廣播队萤,一次處理轮锥,走到deliverToRegisteredReceiverLocked,然后調(diào)用LoadedApk中ReceiverDispatcher的performReceive要尔,在里面構(gòu)造一個叫Args的Runnable舍杜,然后ActivityThread中的H這個Handler去post執(zhí)行。
這樣Runnable的run()方法執(zhí)行赵辕,走到了我們熟悉的onReceive回調(diào)既绩。

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

四、其他東西

以上簡單分析了廣播的整個流程还惠,中間很多細(xì)節(jié)的東西還需要精讀源碼分析饲握,比如有序廣播怎么處理的,靜態(tài)廣播怎么處理的蚕键。
下面列一些結(jié)論性的東西:

// 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.默認(rèn)動態(tài)注冊的廣播ANR時間為60S互拾,說明默認(rèn)動態(tài)注冊的廣播為后臺廣播。
2.無序廣播不會ANR嚎幸,有序廣播會ANR颜矿。但是廣播回調(diào)onReceive如果在主線程耗時,會出現(xiàn)ANR嫉晶,此ANR非廣播ANR骑疆,而是Activity的ANR田篇。
3.ActivityManagerService中有兩個BroadcastQueue,分別是mFgBroadcastQueue和mBgBroadcastQueue,對應(yīng)前臺廣播和后臺廣播箍铭,而每個BroadcastQueue中又含有兩個ArrayList<BroadcastRecord>泊柬,分別為mParallelBroadcasts和mOrderedBroadcasts,對應(yīng)并行廣播和有序廣播诈火。
4.ANR簡單流程如下兽赁,重點(diǎn)在函數(shù)processNextBroadcast:取得下一個有序廣播,Handler發(fā)送延遲ANR msg冷守,如果沒有TIMEOUT刀崖,Handler remove ANR msg,如果TIMEOUT,Handler執(zhí)行AppNotResponding這個Runnable拍摇。

//前臺廣播mTimeoutPeriod為10s亮钦,后臺廣播為60s,通過Handler發(fā)送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,就會執(zhí)行這個混卵,
調(diào)用到broadcastTimeoutLocked映穗,最終通過Handler來post AppNotResponding這個Runnable*/
 @Override
  public void handleMessage(Message msg) {
          switch (msg.what) {
             case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                          broadcastTimeoutLocked(true);
              }
           } break;

當(dāng)然還有個地方也會發(fā)生ANR,和Receiver的個數(shù)有關(guān)系,而且不是通過Handler來發(fā)生的幕随,當(dāng)發(fā)生Timeout時蚁滋,直接調(diào)用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.設(shè)置 intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);這個flag后,廣播不會發(fā)送給已經(jīng)停止的應(yīng)用,系統(tǒng)默認(rèn)是不讓我們的廣播發(fā)送給已經(jīng)停止的應(yīng)用的合陵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市澄阳,隨后出現(xiàn)的幾起案子拥知,更是在濱河造成了極大的恐慌,老刑警劉巖碎赢,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件低剔,死亡現(xiàn)場離奇詭異,居然都是意外死亡肮塞,警方通過查閱死者的電腦和手機(jī)襟齿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枕赵,“玉大人猜欺,你說我怎么就攤上這事】酱埽” “怎么了开皿?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵涧黄,是天一觀的道長。 經(jīng)常有香客問我赋荆,道長笋妥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任窄潭,我火速辦了婚禮春宣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嫉你。我一直安慰自己月帝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布均抽。 她就那樣靜靜地躺著嫁赏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪油挥。 梳的紋絲不亂的頭發(fā)上潦蝇,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機(jī)與錄音深寥,去河邊找鬼攘乒。 笑死,一個胖子當(dāng)著我的面吹牛惋鹅,可吹牛的內(nèi)容都是我干的则酝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼闰集,長吁一口氣:“原來是場噩夢啊……” “哼沽讹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起武鲁,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤爽雄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沐鼠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挚瘟,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年饲梭,在試婚紗的時候發(fā)現(xiàn)自己被綠了乘盖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡憔涉,死狀恐怖订框,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兜叨,我是刑警寧澤布蔗,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布藤违,位于F島的核電站,受9級特大地震影響纵揍,放射性物質(zhì)發(fā)生泄漏顿乒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一泽谨、第九天 我趴在偏房一處隱蔽的房頂上張望璧榄。 院中可真熱鬧,春花似錦吧雹、人聲如沸骨杂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搓蚪。三九已至,卻和暖如春丁鹉,著一層夾襖步出監(jiān)牢的瞬間妒潭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工揣钦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雳灾,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓冯凹,卻偏偏與公主長得像谎亩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宇姚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344