Android應(yīng)用框架之BroadcastReceiver

廣播機(jī)制是Android系統(tǒng)中的一種消息傳播機(jī)制,通過觀察者模式實(shí)現(xiàn)了消息發(fā)送者與消息接收者之間的解耦吵冒。BroadcastReceiver的使用方式有兩種招盲,一種是靜態(tài)注冊(cè),即在Manifest文件中注冊(cè)堪旧,然后在需要發(fā)送廣播時(shí)調(diào)用context.sendBroadcast(intent);奖亚;第二種是動(dòng)態(tài)注冊(cè)淳梦。BroadcastReceiver的使用不是本文的重點(diǎn),本文將著重講解廣播的注冊(cè)過程和消息發(fā)送及接收過程昔字。

1 廣播注冊(cè)過程

廣播的靜態(tài)注冊(cè)是通過PMS(PackageManagerService)來完成的爆袍,其余三大組件也是通過PMS來完成注冊(cè)的。這里重點(diǎn)講一下BroadcastReceiver的動(dòng)態(tài)啟動(dòng)方法。和Activity以及Service一樣螃宙,其啟動(dòng)過程也是通過ContextWrapper-->ContextImpl來完成的蛮瞄。其主要的啟動(dòng)函數(shù)是ContextImpl.registerReceiver:

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) {
    IIntentReceiver rd = null;
    ......
    rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler, mMainThread.getInstrumentation, true);
    ......
    return ActivityManagerNatvice.getDefault().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName, rd, filter, boradcastPermission, userId);
    ......
}

從上面的代碼可以看出主要做了兩件事:

  • mPackageInfo獲取IIntentReceiver對(duì)象,之所以這樣和bindService是一樣的谆扎,因?yàn)樯鲜龅淖?cè)過程是一個(gè)跨進(jìn)程的通信方式挂捅,而BroadcastReceiver作為Android的一個(gè)組件是不能直接跨進(jìn)程傳遞的,所以需要使用IIntentReceiver來中轉(zhuǎn)堂湖。其具體是由LoadedApk.ReceiverDispatcher.InnerReceiver闲先,ReceiverDispatcher內(nèi)部同時(shí)保存了BroadcastReceiverInnerReceiver,所以當(dāng)接收到廣播時(shí)无蜂,ReceiverDispatcher可以很方便地調(diào)用BroadcastReceiver.onReceive()方法伺糠。

  • 通過ActivityManagerNative.getDefault()獲取ActivityManagerService,然后通過AMS來完成廣播的注冊(cè)過程。

接下來具體看一下AMS的registerReceiver具體的實(shí)現(xiàn):

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
    ......
    mRegisterReceivers.put(receiver.asBinder(), rl);
    
    BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);
    rl.add(bf);
    mReceiverResolver.addFilter(bf);
}

2 廣播的發(fā)送和接收過程

廣播的發(fā)送通過ContextImpl.sendBroadcast方法:

public void sendBroadcast(Intent intent) {
    ......
    ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, getUserId());
    ......
}

不出意料斥季,任務(wù)又轉(zhuǎn)到了AMS中训桶,AMS在接收到這個(gè)指令會(huì)調(diào)用內(nèi)部的broadcastIntentLocked方法,在該方法中酣倾,AMS會(huì)根據(jù)intent-filter查找出匹配的廣播接收者舵揭,并通過一系列的條件過濾,并將最終滿足條件的廣播接收者添加到BroadcastQueue中躁锡,然后BroadcastQueue會(huì)將廣播發(fā)送到相應(yīng)的廣播接收者,核心代碼如下:

BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermission, appOp, receivers, resultTo, resultCode, resultData, map, ordered, sticky, false, userId);
......
queue.enqueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();

下面再看一下在BroadcastQueue中發(fā)送廣播scheduleBroadcastsLocked的實(shí)現(xiàn):

public void scheduleBroadcastsLocked(){
    ......
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    ......
}

實(shí)際上BroadcastQueuescheduleBroadcastsLocked方法沒有立即發(fā)送廣播午绳,而是發(fā)送了一個(gè)BROADCAST_INTENT_MSG類型的消息,BroadcastQueue收到該消息后會(huì)調(diào)用processNestBroadcast方法:

while(mParallelBroadcasts.size() > 0) {
    r = mParallelBroadcasts.remove(0);
    r.dispatchTime = SystemClock.uptimeMillis();
    r.dispatchClockTime = System.currentTimeMillis();
    final int N = r.recivers.size();
    for(int i = 0; i < N; i++) {
        Object target = r.receivers.get(i);
        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
    }
    addBroadcastToHistoryLocked(r);
}

可以看到無序廣播存儲(chǔ)在mParallelBroadcasts中映之,系統(tǒng)遍歷該隊(duì)列拦焚,并將廣播發(fā)送給它所有的接收者。具體的發(fā)送工作通過deliverToRegisteredReceiverLocked完成杠输,在該函數(shù)內(nèi)部通過performReceivedLocked來完成:

private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
    ......
    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState);
    ......
}

ApplicationThread的scheduleRegisteredReceiver會(huì)調(diào)用InnerReceiver.performReceive來實(shí)現(xiàn)廣播的接收赎败。而在這個(gè)方法中會(huì)調(diào)用LoadedApk.ReceiverDispatcher.performReceive方法:

public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    ......
    Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser);
    if(!mActivityThread.post(args)) {
        if(mRistered && ordered) {
            IActivityManager mgr = ActivityManagerNative.getDefault();
            args.sendFinished(mgr);
        }
    }
}

在上面的代碼中,會(huì)創(chuàng)建一個(gè)Args對(duì)象抬伺,并通過mActivityThread的post方法來執(zhí)行Args的邏輯螟够,Args實(shí)際上是一個(gè)Runnable接口。mActivityThread是一個(gè)Handler峡钓,其實(shí)就是ActivityThread中的mH,類型是H若河。Args中的run方法有如下幾行代碼:

final BroadcastReceiver receiver = mReceiver;
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);

這個(gè)時(shí)候BroadcastReceiver的onReceive方法才被執(zhí)行能岩,也就接收到廣播了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萧福,一起剝皮案震驚了整個(gè)濱河市拉鹃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖膏燕,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钥屈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坝辫,警方通過查閱死者的電腦和手機(jī)篷就,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來近忙,“玉大人竭业,你說我怎么就攤上這事〖吧幔” “怎么了未辆?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锯玛。 經(jīng)常有香客問我咐柜,道長(zhǎng),這世上最難降的妖魔是什么攘残? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任拙友,我火速辦了婚禮,結(jié)果婚禮上肯腕,老公的妹妹穿的比我還像新娘献宫。我一直安慰自己,他們只是感情好实撒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布姊途。 她就那樣靜靜地躺著,像睡著了一般知态。 火紅的嫁衣襯著肌膚如雪捷兰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天负敏,我揣著相機(jī)與錄音贡茅,去河邊找鬼。 笑死其做,一個(gè)胖子當(dāng)著我的面吹牛顶考,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妖泄,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼驹沿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蹈胡?” 一聲冷哼從身側(cè)響起渊季,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤朋蔫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后却汉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驯妄,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年合砂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了青扔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡既穆,死狀恐怖赎懦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幻工,我是刑警寧澤励两,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站囊颅,受9級(jí)特大地震影響当悔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜踢代,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一盲憎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胳挎,春花似錦饼疙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至医窿,卻和暖如春磅甩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姥卢。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工卷要, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人独榴。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓僧叉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親棺榔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彪标,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 在Activity生命周期管理 以及 插件加載機(jī)制 中我們?cè)敿?xì)講述了插件化過程中對(duì)于Activity組件的處理方式...
    weishu閱讀 2,256評(píng)論 3 18
  • 前言 本來想寫一下廣播的,發(fā)現(xiàn)查閱后有整理的不錯(cuò)的掷豺,只好轉(zhuǎn)載圖個(gè)簡(jiǎn)便捞烟,日后好復(fù)習(xí)轉(zhuǎn)載:http://www.cnb...
    提升即效率閱讀 1,385評(píng)論 0 10
  • 注冊(cè)方式 1.動(dòng)態(tài)注冊(cè) 另外,注冊(cè)廣播可以通過IntentFilter的 setPriority設(shè)置廣播的優(yōu)先級(jí)当船。...
    jacky123閱讀 758評(píng)論 0 3
  • 到了行事當(dāng)日题画,天剛放明,蒿荇與張?jiān)⑿懿恍莅凑沼?jì)劃帶了五十名好手德频,喬裝成天師教前來支援剿魔的弟子苍息,在王陽華的帶領(lǐng)下...
    是靜顏呀閱讀 215評(píng)論 0 0
  • ?心享事成 心今日心享~享受就瘦 當(dāng)我享受我的身材時(shí),身體會(huì)自動(dòng)塑形壹置! 評(píng)判身材竞思,是身材走形的真正原因! 我的身材...
    axjl如意閱讀 258評(píng)論 0 0