Android廣播接收機(jī)制

BroadcastReceiver作為Android四大組件之一埃难,在Android日常開發(fā)中有著不可或缺的作用,主要做以下用途:

  • 應(yīng)用內(nèi)消息傳遞涤久,多進(jìn)程之間消息通信
  • 應(yīng)用之間消息傳遞涡尘,不同App之間消息傳遞
  • Android系統(tǒng)和應(yīng)用之間消息傳遞,比如接收網(wǎng)絡(luò)狀態(tài)通知响迂,開機(jī)關(guān)機(jī)通知等

1. 廣播的分類

1.1 Normal Broadcast 普通廣播

普通廣播,也叫標(biāo)準(zhǔn)廣播考抄,由開發(fā)者自定義發(fā)出,執(zhí)行在和接受者異步線程的廣播蔗彤,在廣播發(fā)出后川梅,所有的接受者均可以接受到該廣播通知,接受者之間沒有先后順序然遏,且無法被截?cái)嗥锻尽F胀ㄗ?cè)形式有兩種,一種為動(dòng)態(tài)注冊(cè)待侵,一種為靜態(tài)注冊(cè)丢早。

1.1.1 動(dòng)態(tài)注冊(cè)廣播

動(dòng)態(tài)注冊(cè)可以在java代碼中,通過繼承自BroadcastReceiver的類實(shí)例秧倾,以及指定Action的IntentFilter去進(jìn)行注冊(cè)怨酝,這里需要注意,在Destroy的時(shí)候需要將該廣播進(jìn)行unRegister中狂,否則會(huì)導(dǎo)致內(nèi)存泄漏等問題凫碌。

    /**
     * 注冊(cè)廣播接收機(jī)
     */
    private void registerBroadcast() {
        broadcastReceiver = new TestBroadcastReceiver();
        // 指定Action,這里使用系統(tǒng)廣播Action胃榕,也可以使用自定義
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED);
        registerReceiver(broadcastReceiver, intentFilter);
    }

這里使用的注冊(cè)方法盛险,還有其余構(gòu)造參數(shù)瞄摊,例如:

    /**
     * 廣播注冊(cè)
     * @param receiver 廣播接收器
     * @param filter 接受廣播的條件過濾
     * @param broadcastPermission 接受廣播的權(quán)限,只能接受相應(yīng)權(quán)限的廣播發(fā)送器發(fā)送的廣播
     * @param scheduler 默認(rèn)為主線程苦掘,也可使用其他線程創(chuàng)建的handler
     * @return
     */
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler)
1.1.2 靜態(tài)注冊(cè)廣播

在清單文件AndroidManifest.xml中進(jìn)行動(dòng)態(tài)注冊(cè)换帜,使用<receiver>標(biāo)簽進(jìn)行聲明,并在標(biāo)簽內(nèi)鹤啡,使用<inter-filter>標(biāo)簽設(shè)置過濾器惯驼。

<receiver
    android:name="com.jd.test.receiver.TestBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
   <intent-filter>
       <action android:name="android.intent.action.ACTION_AIRPLANE_MODE_CHANGED" />
   </intent-filter>
</receiver>

其中exported設(shè)置該廣播能否接受其他應(yīng)用的廣播消息,前提是filter中name名稱一致递瑰。當(dāng)廣播中有默認(rèn)IntentFilter祟牲,該值默認(rèn)為true,否則為false抖部。同樣還可以設(shè)置:

  • android:permission 廣播權(quán)限設(shè)置说贝,具有相應(yīng)廣播發(fā)送權(quán)限的廣播發(fā)送方,發(fā)送的廣播才能被該廣播接收器接收慎颗。
  • android:process 設(shè)置廣播運(yùn)行所處的進(jìn)程乡恕,默認(rèn)為app進(jìn)程,可以指定獨(dú)立的進(jìn)程

這里我們?cè)O(shè)置的監(jiān)聽信息為系統(tǒng)廣播俯萎,監(jiān)聽飛行模式的變化傲宜。可以在onReceive中打印輸出變化狀態(tài)

1.2 System Broadcast 系統(tǒng)廣播

系統(tǒng)廣播夫啊,Android系統(tǒng)內(nèi)設(shè)置了許多個(gè)系統(tǒng)廣播函卒,比如開關(guān)機(jī),網(wǎng)絡(luò)狀態(tài)變化等涮母,每一個(gè)廣播谆趾,均有一個(gè)相應(yīng)的intent-filter,其中包含了具體的action叛本,由Android系統(tǒng)發(fā)出沪蓬。應(yīng)用內(nèi)注冊(cè)了相應(yīng)的BroadcastReceiver,會(huì)收到該通知,例如上述設(shè)置的監(jiān)聽系統(tǒng)飛行模式的廣播来候□尾妫可以監(jiān)測(cè)的還有其他類型的廣播:

// 關(guān)閉或打開飛行模式時(shí)的廣播
Intent.ACTION_AIRPLANE_MODE_CHANGED;

// 充電狀態(tài),或者電池的電量發(fā)生變化
Intent.ACTION_BATTERY_CHANGED;
 
// 表示電池電量低
Intent.ACTION_BATTERY_LOW;

// 表示電池電量充足营搅,即從電池電量低變化到飽滿時(shí)會(huì)發(fā)出廣播
Intent.ACTION_BATTERY_OKAY;
 
// 在系統(tǒng)啟動(dòng)完成后云挟,這個(gè)動(dòng)作被廣播一次(只有一次)
Intent.ACTION_BOOT_COMPLETED;

// 這樣的廣播還有很多
........

在Android8.0的升級(jí)后,對(duì)廣播機(jī)制進(jìn)行了限制转质,因?yàn)榇罅康膹V播通過靜態(tài)注冊(cè)园欣,會(huì)導(dǎo)致所有應(yīng)用快速地連續(xù)消耗資源,降低用戶體驗(yàn)休蟹,所以鼓勵(lì)使用動(dòng)態(tài)注冊(cè)沸枯。

兼容:

如果遇到靜態(tài)廣播無法修改日矫,或者需要兼容靜態(tài)之前靜態(tài)廣播時(shí),可以使用intent.setComponent()方法绑榴,在發(fā)送廣播之前哪轿,進(jìn)行設(shè)置,該方法可以在8.0上兼容之前版本設(shè)置隱式廣播翔怎。

1.3 Ordered broadcast 有序廣播

有序廣播是一種同步執(zhí)行的廣播窃诉,在廣播發(fā)出后,同一時(shí)間只有一個(gè)廣播接收器會(huì)接受到廣播赤套,優(yōu)先級(jí)較高的接收器飘痛,先接受到廣播。此時(shí)該廣播接收器于毙,可以選擇敦冬,截?cái)鄰V播,還是繼續(xù)傳遞廣播唯沮,如果選擇截?cái)鄰V播,則后續(xù)優(yōu)先級(jí)較低的廣播堪遂,將不會(huì)收到該廣播信息介蛉。

// 設(shè)置優(yōu)先級(jí)
intentFilter.setPriority(20);
        
// 判斷是否為有序廣播
broadcastReceiver.isOrderedBroadcast();

// 取消廣播
broadcastReceiver.abortBroadcast();

取消廣播實(shí)際為設(shè)置廣播內(nèi)部PendingResult的標(biāo)志位為取消

mPendingResult.mAbortBroadcast = true;

上述,簡(jiǎn)單的描述了廣播的使用方法溶褪,顯式注冊(cè)和隱式注冊(cè)币旧。而廣播是在設(shè)計(jì)之初,就是針對(duì)應(yīng)用與系統(tǒng)之間猿妈,應(yīng)用與應(yīng)用之間吹菱,應(yīng)用內(nèi)部等進(jìn)行通信,但是也正是這樣的設(shè)計(jì)模式彭则,對(duì)使用BroadcastReceiver的應(yīng)用程序鳍刷,帶來了風(fēng)險(xiǎn)。如果有人通過某些手段俯抖,得到了你應(yīng)用監(jiān)聽的廣播名稱和過濾器名稱输瓜,那么便可以通過發(fā)送指定的廣播,對(duì)你的應(yīng)用造成“破壞”芬萍。為了防止出現(xiàn)這樣的問題尤揣,我們需要做到:

  • 設(shè)置exported為false,默認(rèn)至接受當(dāng)前應(yīng)用的廣播柬祠,其他應(yīng)用發(fā)出的廣播不接受北戏。
  • 在自定義廣播發(fā)送和接收時(shí),添加指定的permission漫蛔,進(jìn)行校驗(yàn)
  • 在應(yīng)用內(nèi)部發(fā)送廣播時(shí)嗜愈,對(duì)包名進(jìn)行指定旧蛾,調(diào)用intent.setPackage()這樣只對(duì)指定包中注冊(cè)的接收器進(jìn)行接受
  • 如果只是應(yīng)用內(nèi)部廣播的話,可以直接使用LocalBroadcastManager芝硬,本地廣播蚜点,保證廣播發(fā)送者和接收者在同一個(gè)App。

1.4 Local Broadcast 應(yīng)用內(nèi)廣播

應(yīng)用內(nèi)廣播為Google向開發(fā)者提供的一個(gè)輕量型廣播拌阴,只能用于單個(gè)應(yīng)用內(nèi)消息傳遞绍绘,無法靜態(tài)注冊(cè),僅支持動(dòng)態(tài)注冊(cè)迟赃。

// 使用LocalBroadcastManager發(fā)送廣播
Intent intent = new Intent();
intent.setAction(TEST_ACTION);
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);

只能使用LocalBroadcastManager自己的單例進(jìn)行廣播發(fā)送陪拘,也只能對(duì)LocalBroadcastManager注冊(cè)過的接收器,才能收到該廣播纤壁。

// 指定Action左刽,這里使用系統(tǒng)廣播Action,也可以使用自定義
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TEST_ACTION);

// BroadcastReceiver
broadcastReceiver = new TestBroadcastReceiver();

// 注冊(cè)廣播
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);

在Activity銷毀時(shí)酌媒,千萬記得將當(dāng)前注冊(cè)廣播進(jìn)行銷毀欠痴,否則可能會(huì)造成內(nèi)存泄漏等問題。

// 注銷廣播
if (broadcastReceiver != null) {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}

推薦在應(yīng)用內(nèi)部使用LocalBroadcastManager秒咨,它有以下優(yōu)點(diǎn):

  • 避免自定義廣播被外部應(yīng)用意外接收
  • 避免應(yīng)用內(nèi)部信息泄漏
  • 使用主線程Looper進(jìn)行循環(huán)發(fā)送廣播喇辽,高效簡(jiǎn)潔

最后,在老版本中雨席,這里還有一種廣播類型菩咨,Sticky Broadcast,也就是粘滯性廣播陡厘,由于該廣播在Android5.0也就是API 21之后已經(jīng)不再使用抽米,因此,這里對(duì)該廣播將不再贅述糙置。

1.5 注意事項(xiàng)

對(duì)于需要使用BroadcastReceiver的地方云茸,為了安全高效,需要注意以下幾點(diǎn):

  • 設(shè)置exported為false罢低,不監(jiān)聽其他應(yīng)用廣播
  • 權(quán)限設(shè)置查辩,發(fā)送廣播時(shí)区匠,使用permission進(jìn)行校驗(yàn)
  • 包名設(shè)置驹针,使用intent.setPackage()指定包名
  • 使用LocalBroadcastManager
  • onReceive中不能執(zhí)行耗時(shí)操作枢劝,如果耗時(shí)超過10s會(huì)彈出ANR氧秘。
  • onReceive中的Context括儒,如果是動(dòng)態(tài)注冊(cè)令漂,則為注冊(cè)組建的Context楼咳,例如Activity或者是Service娃弓,如果是靜態(tài)注冊(cè)辟汰,則為ReceiverRestrictedContext列敲,在這里如果需要啟動(dòng)Activity阱佛,需要添加Flag——Intent.FLAG_ACTIVITY_NEW_TASK
  • 普通廣播是并列無序執(zhí)行的,有序廣播是串行有序執(zhí)行的戴而,優(yōu)先級(jí)較高的先執(zhí)行凑术。
  • 使用Context.sendBroadcast發(fā)送的廣播,如果依次注冊(cè)了三個(gè)廣播接收者A/B/C,那么接受順序是C/B/A所意,即后注冊(cè)的先收到消息淮逊。
  • 使用LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent)發(fā)送廣播的,如果依次注冊(cè)了三個(gè)廣播接收者A/B/C,那么接受順序是A/B/C扶踊,即先注冊(cè)的先收到消息
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泄鹏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子秧耗,更是在濱河造成了極大的恐慌备籽,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件分井,死亡現(xiàn)場(chǎng)離奇詭異车猬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)尺锚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門诈唬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缩麸,你說我怎么就攤上這事∩氖福” “怎么了杭朱?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吹散。 經(jīng)常有香客問我弧械,道長(zhǎng),這世上最難降的妖魔是什么空民? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任刃唐,我火速辦了婚禮,結(jié)果婚禮上界轩,老公的妹妹穿的比我還像新娘画饥。我一直安慰自己,他們只是感情好浊猾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布抖甘。 她就那樣靜靜地躺著,像睡著了一般葫慎。 火紅的嫁衣襯著肌膚如雪衔彻。 梳的紋絲不亂的頭發(fā)上薇宠,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音艰额,去河邊找鬼澄港。 笑死,一個(gè)胖子當(dāng)著我的面吹牛柄沮,可吹牛的內(nèi)容都是我干的回梧。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼铡溪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼漂辐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起棕硫,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤髓涯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后哈扮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纬纪,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年滑肉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了包各。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡靶庙,死狀恐怖问畅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情六荒,我是刑警寧澤护姆,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站掏击,受9級(jí)特大地震影響卵皂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砚亭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一灯变、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捅膘,春花似錦添祸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春蔬咬,著一層夾襖步出監(jiān)牢的瞬間鲤遥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工林艘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盖奈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓狐援,卻偏偏與公主長(zhǎng)得像钢坦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啥酱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

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