Android四大組件(三):BroadcastReceiver

BroadcastReceiver代表廣播消息接收器。從代碼實現(xiàn)角度來看柿究,BroadcastReceiver非常類似于事件編程中的監(jiān)聽器斑司。與普通事件監(jiān)聽器不同的是:普通事件監(jiān)聽器監(jiān)聽的事件源是程序中的對象绽媒;而BroadcastReceiver監(jiān)聽的事件源是Android應(yīng)用中的其他組件。

BroadcastReceiver的生命周期

與Activity傲霸、Service具有完整的生命周期不同疆瑰,BroadcastReceiver本質(zhì)上是一個系統(tǒng)級的監(jiān)聽器——它專門負(fù)責(zé)監(jiān)聽各程序所發(fā)出的Broadcast。

通常我們所使用的OnXxxListener只是程序級別的監(jiān)聽器昙啄,這些監(jiān)聽器運行在指定程序所在進(jìn)程中穆役,當(dāng)程序退出時,OnXxxListener監(jiān)聽器也就隨之關(guān)閉了梳凛。但BroadcastReceiver屬于系統(tǒng)級的監(jiān)聽器耿币,它擁有自己的進(jìn)程。

只要存在與之匹配的Intent被廣播出來韧拒,BroadcastReceiver就會被激發(fā)淹接。系統(tǒng)會創(chuàng)建對應(yīng)的BroadcastReceiver的實例,并自動觸發(fā)它的onReceive()方法叛溢,onReceive()方法執(zhí)行完成后塑悼,BroadcastReceiver的實例就會被銷毀。如果onReceive()方法不能再10秒內(nèi)執(zhí)行完成楷掉,Android會認(rèn)為程序無響應(yīng)厢蒜,并彈出ANR異常。所以不要在onReceive()方法里執(zhí)行耗時的操作。

如果確實需要根據(jù)廣播來完成一項比較耗時的操作郭怪,可以考慮通過Intent啟動一個Service來完成該操作支示。不應(yīng)考慮直接在onReceive()方法里面使用新線程去完成耗時的操作刊橘,因為BroadcastReceiver本身的生命周期很短鄙才,可能出現(xiàn)子線程還沒結(jié)束,BroadcastReceiver就已經(jīng)退出了的情況(內(nèi)存泄漏)促绵。

如果BroadcastReceiver所在的進(jìn)程結(jié)束攒庵,雖然該進(jìn)程內(nèi)還有用戶啟動的新線程,但由于進(jìn)程內(nèi)不包含任何活動組件败晴,因此系統(tǒng)可能在內(nèi)存緊張是優(yōu)先結(jié)束該進(jìn)程浓冒,這樣就可能導(dǎo)致BroadcastReceiver啟動的子線程不能執(zhí)行完成。進(jìn)程優(yōu)先級的問題


如何使用BroadcastReceiver

創(chuàng)建BroadcastReceiver
1尖坤、開發(fā)者需要實現(xiàn)自己的BroadcastReceiver子類稳懒,并重寫onReceive(Context context, Intent intent);

package com.richinfo.broadcastreceiverdemo.broadcastreceive;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * Created by Administrator on 2019/1/31.
 */
public class SimpleBroadcastReceive extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        System.out.println("Received");
    }
}

2慢味、指定BroadcastReceiver能匹配的Intent场梆,并注冊廣播接受者。有兩種方式:

  • 動態(tài)注冊廣播(在代碼中指定):
    IntentFilter intentFilter = new IntentFilter();
    //設(shè)置要過濾的Intent類型
    intentFilter.addAction("com.richinfo.broadcastreceiverdemo.SIMPLE_BROADCAST_RECEIVED");
    //設(shè)置當(dāng)前廣播接受者的優(yōu)先級(有序廣播時)
    //intentFilter.setPriority(1);
    SimpleBroadcastReceiver receiver = new SimpleBroadcastReceiver();
    registerReceiver(receiver, intentFilter);
  • 靜態(tài)注冊廣播(在Manifest.xml文件中配置):
        <receiver android:name=".broadcastreceive.SimpleBroadcastReceiver">
            <intent-filter>
                <action android:name="com.richinfo.broadcastreceiverdemo.SIMPLE_BROADCAST_RECEIVED"/>
            </intent-filter>
        </receiver>
  • 兩種方式的區(qū)別:
    ①靜態(tài)注冊的廣播纯路,必需單獨成一個類或油,不能像動態(tài)注冊那樣寫在某個界面里。否則當(dāng)監(jiān)聽到網(wǎng)絡(luò)狀態(tài)變化的時候驰唬,會拋出異常顶岸;
    ②動態(tài)注冊的廣播會受Activity的生命周期的影響, 當(dāng)Activity銷毀的時候叫编,廣播就失效了辖佣;
    ③靜態(tài)注冊的廣播,即使Activity銷毀了搓逾,仍然可以收到廣播卷谈。甚至于即使殺死進(jìn)程,仍然可以收到廣播恃逻;
    ④當(dāng)廣播為有序廣播時:優(yōu)先級高的先接收(不分靜態(tài)和動態(tài))雏搂。同優(yōu)先級的廣播接收器,動態(tài)優(yōu)先于靜態(tài)寇损;
    ⑤同優(yōu)先級的同類廣播接收器凸郑,靜態(tài):先掃描的優(yōu)先于后掃描的,動態(tài):先注冊的優(yōu)先于后注冊的矛市。

發(fā)送廣播

    Intent intent = new Intent();
    //需要設(shè)置跟BroadcastReceiver的IntentFilter過濾條件相同的action
    intent.setAction("com.richinfo.broadcastreceiverdemo.SIMPLE_BROADCAST_RECEIVED");
    //需要傳遞的數(shù)據(jù)
    intent.putExtra("msg", "發(fā)送一個SimpleMessage");
    sendBroadcast(intent);

廣播的分類

1芙沥、NormalBroadcast(普通廣播):是完全異步的,邏輯上是可以在同一時刻被所有接收者接收到,消息傳遞的效率比較高而昨。但缺點是接收者不能將處理結(jié)果傳遞給下一個接收者救氯,并且無法終止(攔截)Broadcast Intent的傳播。我們可以通過sendBroadcast()發(fā)送Normal Broadcast歌憨。

2着憨、Ordered Broadcast(有序廣播):廣播接收者將按預(yù)先聲明的優(yōu)先級依次接受Broadcast。如:A的級別高于B务嫡、B的級別高于C甲抖,那么,Broadcast先傳給A心铃,再傳給B准谚,最后傳給C。優(yōu)先級別聲明在<intent-filter.../>元素的android:priority屬性中去扣,數(shù)越大優(yōu)先級別越高(-1000~1000)柱衔;也可以通過調(diào)用IntentFilter對象setPriority()進(jìn)行設(shè)置。

有序廣播的接收者可以將數(shù)據(jù)傳遞給下一個接收者愉棱,如:A得到Broadcast后唆铐,可以往它的結(jié)果對象中存入數(shù)據(jù)(通過調(diào)用setResultExtras(Bundle)),當(dāng)Broadcast傳給B時羽氮,B可以從A的結(jié)果中得到A存入的數(shù)據(jù)(通過調(diào)用getResultExtras(true))或链。

有序廣播也可以通過在onReceive()方法里面調(diào)用abortBroadcast()被終止,一旦有序廣播被優(yōu)先級更高的接收者終止档押,后面的接收者就無法收到Broadcast澳盐。

3、Sticky Broadcast(遲滯廣播):發(fā)出的廣播會一直滯留(等待)令宿,以便有人注冊這則廣播消息后能盡快的收到這條廣播叼耙。其他功能與sendBroadcast相同。使用sendStickyBroadcast 發(fā)送廣播需要獲得BROADCAST_STICKY permission粒没,如果沒有這個permission則會拋出異常筛婉。sendStickyBroadcast只保留最后一條廣播,并且一直保留下去癞松,這樣即使已經(jīng)處理了這條廣播但當(dāng)再一次注冊這條廣播后依然可以收到它爽撒。這個方法在android-23被標(biāo)注為Deprecated。


使用廣播的風(fēng)險以及更安全的本地廣播

使用廣播時存在的風(fēng)險
在Android系統(tǒng)中,BroadcastReceiver的設(shè)計初衷就是從全局考慮的响蓉,可以方便應(yīng)用程序和系統(tǒng)硕勿、應(yīng)用程序之間、應(yīng)用程序內(nèi)的通信枫甲,所以對單個應(yīng)用程序而言BroadcastReceiver是存在安全性問題的:

問題一:當(dāng)應(yīng)用程序發(fā)送某個廣播時系統(tǒng)會將發(fā)送的Intent與系統(tǒng)中所有注冊的BroadcastReceiver的IntentFilter進(jìn)行匹配源武,若匹配成功則執(zhí)行相應(yīng)的onReceive函數(shù)扼褪。可能會出現(xiàn)惡意偽造廣播的問題粱栖,存在安全隱患话浇。

解決辦法:可以通過類似sendBroadcast(Intent,String)的接口在發(fā)送廣播時指定接收者必須具備的permission闹究♂Q拢或通過Intent.setPackage設(shè)置廣播僅對某個程序有效。

問題二:當(dāng)應(yīng)用程序注冊了某個廣播時跋核,即便設(shè)置了IntentFilter還是會收到來自其他應(yīng)用程序的廣播進(jìn)行匹配判斷岖瑰。這樣會導(dǎo)致匹配效率低下叛买。

解決辦法:對于動態(tài)注冊的廣播可以通過類似registerReceiver(BroadcastReceiver砂代, IntentFilter,String率挣,android.os.Handler)的接口指定發(fā)送者必須具備的permission刻伊;對于靜態(tài)注冊的廣播可以通過android:exported="false"屬性表示接收者對外部應(yīng)用程序不可用,即不接受外部的廣播

本地廣播
在Android的Support-v4包中提供了LocalBroadcastManager工具類椒功,幫助開發(fā)者解決進(jìn)程內(nèi)進(jìn)行的局部廣播發(fā)送與注冊捶箱。它主要有有一些幾點優(yōu)勢:

  • 因廣播數(shù)據(jù)在本應(yīng)用范圍內(nèi)傳播,你不用擔(dān)心隱私數(shù)據(jù)泄露的問題动漾;
  • 不用擔(dān)心別的應(yīng)用偽造廣播丁屎,造成安全隱患;
  • 相比在系統(tǒng)內(nèi)發(fā)送全局廣播旱眯,它更高效晨川。

具體使用方法:

  • 注冊本地廣播:
package com.richinfo.broadcastreceiverdemo;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import com.richinfo.broadcastreceiverdemo.broadcastreceive.SimpleBroadcastReceiver;

public class MainActivity extends AppCompatActivity {

    private Button btnRegist;
    private Button btnSecond;

    private LocalBroadcastManager localBroadcastManager;
    private SimpleBroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        IntentFilter intentFilter = new IntentFilter();
        //設(shè)置要過濾的Intent類型
        intentFilter.addAction("com.richinfo.broadcastreceiverdemo.SIMPLE_BROADCAST_RECEIVED");
        //設(shè)置當(dāng)前廣播接受者的優(yōu)先級(有序廣播時)
        //intentFilter.setPriority(1);
        receiver = new SimpleBroadcastReceiver();
        localBroadcastManager.registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //一定不要忘記取消注冊的操作,否則會導(dǎo)致內(nèi)存泄漏
        localBroadcastManager.unregisterReceiver(receiver);
    }
}
  • 發(fā)送本地廣播:
package com.richinfo.broadcastreceiverdemo;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;

public class SecondActivity extends AppCompatActivity {
    private Button btnSend;
    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        btnSend = (Button) findViewById(R.id.btn_send);
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                //需要設(shè)置跟BroadcastReceiver的IntentFilter過濾條件相同的action
                intent.setAction("com.richinfo.broadcastreceiverdemo.SIMPLE_BROADCAST_RECEIVED");
                //需要傳遞的數(shù)據(jù)
                intent.putExtra("msg", "發(fā)送一個SimpleMessage");
                localBroadcastManager.sendBroadcast(intent);
            }
        });
    }
}

Android系統(tǒng)廣播

BroadcastReceiver一個重要的用途删豺,就是接受系統(tǒng)廣播共虑。如果應(yīng)用需要在系統(tǒng)特定時刻執(zhí)行某些操作,就可以通過監(jiān)聽系統(tǒng)廣播來實現(xiàn)呀页。Android的大量系統(tǒng)事件都會對外發(fā)送標(biāo)準(zhǔn)廣播妈拌。一下是Android常見的廣播Action常量:

  • ACTION_TIME_CHANGED:系統(tǒng)時間被改變;
  • ACTION_DATE_CHANGED:系統(tǒng)日期被改變蓬蝶;
  • ACTION_TIMEZONE_CHANGED:系統(tǒng)時區(qū)被改變尘分;
  • ACTION_BOOT_COMPLETED:系統(tǒng)啟動完成;
  • ACTION_PACKAGE_ADDED:系統(tǒng)添加包丸氛;
  • ACTION_PACKAGE_CHANGED:系統(tǒng)的包改變培愁;
  • ACTION_PACKAGE_REMOVED:系統(tǒng)的包被刪除;
  • ACTION_PACKAGE_RESTARTED:系統(tǒng)的包被重啟雪位;
  • ACTION_PACKAGE_DATA_CLEARED:系統(tǒng)的包數(shù)據(jù)被清空竭钝;
  • ACTION_BATTERY_CHANGED:電池電量改變梨撞;
  • ACTION_BATTERY_LOW:電池電量低;
  • ACTION_POWER_CONNECTED:系統(tǒng)連接電源香罐;
  • ACTION_POWER_DISCONNECTED:系統(tǒng)連接電源卧波;
  • ACTION_SHUTDOWN:系統(tǒng)被關(guān)閉;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坡氯,更是在濱河造成了極大的恐慌缺猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霍比,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)偿曙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羔巢,“玉大人望忆,你說我怎么就攤上這事「透眩” “怎么了启摄?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長幽钢。 經(jīng)常有香客問我歉备,道長,這世上最難降的妖魔是什么匪燕? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任蕾羊,我火速辦了婚禮,結(jié)果婚禮上谎懦,老公的妹妹穿的比我還像新娘肚豺。我一直安慰自己,他們只是感情好界拦,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布吸申。 她就那樣靜靜地躺著,像睡著了一般享甸。 火紅的嫁衣襯著肌膚如雪截碴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天蛉威,我揣著相機(jī)與錄音日丹,去河邊找鬼。 笑死蚯嫌,一個胖子當(dāng)著我的面吹牛哲虾,可吹牛的內(nèi)容都是我干的丙躏。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼束凑,長吁一口氣:“原來是場噩夢啊……” “哼晒旅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汪诉,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤废恋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扒寄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鱼鼓,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年该编,在試婚紗的時候發(fā)現(xiàn)自己被綠了迄本。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡上渴,死狀恐怖岸梨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稠氮,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布半开,位于F島的核電站隔披,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寂拆。R本人自食惡果不足惜奢米,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纠永。 院中可真熱鬧鬓长,春花似錦、人聲如沸尝江。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炭序。三九已至啤覆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惭聂,已是汗流浹背窗声。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留辜纲,地道東北人笨觅。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓拦耐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親见剩。 傳聞我的和親對象是個殘疾皇子揩魂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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