淺談Android Broadcast

前言:本文所寫的是博主的個(gè)人見解厢岂,如有錯(cuò)誤或者不恰當(dāng)之處,歡迎私信博主阳距,加以改正塔粒! 原文鏈接demo鏈接

廣播簡(jiǎn)述

Android應(yīng)用程序可以發(fā)送或者接收來自 Android 系統(tǒng)和其他 Android 應(yīng)用程序的廣播消息筐摘,類似于發(fā)布訂閱設(shè)計(jì)模式卒茬。當(dāng)感興趣的事件發(fā)生時(shí),這些廣播被發(fā)送咖熟。例如扬虚,Android 系統(tǒng)在各種系統(tǒng)事件發(fā)生時(shí)發(fā)送廣播,比如系統(tǒng)啟動(dòng)或者設(shè)備開始充電等球恤。應(yīng)用程序還可以發(fā)送自定義的廣播,比如通知其他應(yīng)用可能感興趣的內(nèi)容(例如荸镊,一些新數(shù)據(jù)已被下載)咽斧。

應(yīng)用程序可以注冊(cè)接收特定的廣播,當(dāng)發(fā)送廣播時(shí)躬存,系統(tǒng)自動(dòng)將廣播路由到訂閱該特定類型廣播的應(yīng)用程序张惹。一般來說,廣播可以用作跨應(yīng)用程序和正常用戶流之外的消息傳遞系統(tǒng)岭洲。

系統(tǒng)廣播

當(dāng)發(fā)生各種系統(tǒng)事件時(shí),系統(tǒng)會(huì)自動(dòng)發(fā)送廣播宛逗,例如系統(tǒng)切換飛行模式時(shí),系統(tǒng)廣播被發(fā)送到所有接收訂閱事件的應(yīng)用程序盾剩。

廣播消息本身被包裹在一個(gè) Intent 對(duì)象的動(dòng)作字符串標(biāo)識(shí)(例如 android.intent.action.AIRPLANE_MODE )雷激。這個(gè) Intent 還可以包括附加到其額外字段中的附加信息替蔬。比如,"飛行模式" 的 Intent 包括一個(gè) boolean 額外指示是否 "飛行模式"屎暇。

系統(tǒng)廣播行為的完整列表承桥,請(qǐng)參閱 Android SDK 中的 BROADCAST_ACTIONS.TXT 文件。每個(gè)廣播行為都有與之關(guān)聯(lián)的常量字段根悼,例如常量 ACTION_AIRPLANE_MODE_CHANGED 的值為 android.intent.action.AIRPLANE_MODE凶异,每個(gè)廣播動(dòng)作的文檔都可以在其相關(guān)聯(lián)的常量字段中獲得。

注:系統(tǒng)廣播更改
Android 7 和更高的不再發(fā)送以下系統(tǒng)廣播挤巡。這種優(yōu)化影響所有的應(yīng)用程序剩彬,不僅那些針對(duì)Android 7。

  • ACTION_NEW_PICTURE
  • ACTION_NEW_VIDEO
    針對(duì) Android 7 的應(yīng)用程序(API級(jí)別24)和更高的必須登記以下的廣播代碼注冊(cè)廣播接收器(BroadcastReceiver 矿卑,
    IntentFilter)喉恋。在清單中聲明接收器不起作用。
  • CONNECTIVITY_ACTION

注冊(cè)接收廣播

應(yīng)用程序可以通過兩種方式接收廣播:通過清單聲明的接收者和上下文注冊(cè)的接收者

  1. 清單聲明的接收器(靜態(tài)注冊(cè))

在清單中聲明廣播接收者粪摘,可以通過下面的步驟:
(1)在應(yīng)用程序的清單中指定 <receiver> 元素

<receiver
       android:name=".DemoBroadcastReceiver"
       android:exported="true">
     <intent-filter>
         <action android:name="android.intent.action.BOOT_COMPLETED"/>
         <action android:name="android.intent.action.INPUT_METHOD_CHANGED"/>
     </intent-filter>
         
</receiver>

intent-filter (意圖過濾器) 指定你的接收者訂閱的廣播操作瀑晒。

(2)繼承 BroadcastReceiver 并實(shí)現(xiàn) onReceive(Context,Intent) 方法徘意,請(qǐng)看下面的示例:

public class DemoBroadcastReceiver extends BroadcastReceiver {
     private static final String TAG = "DemoBroadcastReceiver";
     
     @Override
     public void onReceive(Context context, Intent intent) {
         StringBuilder sb = new StringBuilder();
         sb.append("Action: " + intent.getAction() + "\n");
         sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
         String log = sb.toString();
         Log.d(TAG, log);
         Toast.makeText(context, log, Toast.LENGTH_LONG).show();
    }
    
}

當(dāng)應(yīng)用程序安裝時(shí)苔悦,系統(tǒng)包管理器注冊(cè)接受器,然后接收器將成為你的應(yīng)用程序的單獨(dú)入口椎咧,這意味著如果應(yīng)用程序當(dāng)前未運(yùn)行玖详,系統(tǒng)可以啟動(dòng)你的應(yīng)用程序并發(fā)送廣播。

系統(tǒng)將創(chuàng)建一個(gè)新的 BroadcastReceiver 組件對(duì)象來處理它接收的每個(gè)廣播勤讽。這個(gè)對(duì)象僅對(duì)調(diào)用 onReceive(Context蟋座,Intent)的時(shí)候有效。當(dāng)你在代碼從該方法返回脚牍,系統(tǒng)將認(rèn)為組件不再活動(dòng)向臀。

  1. context 注冊(cè)接收者(動(dòng)態(tài)注冊(cè))

    用context注冊(cè)接收器,可以通過以下幾個(gè)步驟:

    (1) 創(chuàng)建 BroadcastReceiver 并實(shí)例化

     BroadcastReceiver broadcastReceiver = new DemoBroadcastReceiver();
    

    (2) 創(chuàng)建一個(gè) IntentFilter 并調(diào)用 registerReceiver( BroadcastReceiver , IntentFilter ) 來注冊(cè)接收器

     IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
     intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
     this.registerReceiver(broadcastReceiver,intentFilter);
    

    注意:要注冊(cè)本地廣播诸狭,請(qǐng)改用 LocalBroadcastManager.registerReceiver( BroadcastReceiver , IntentFilter )券膀。

    如果context注冊(cè)有效時(shí),context注冊(cè)的接收者就可以接收廣播驯遇。例如你在 Activity 的 context 中注冊(cè)芹彬,只要 Activity 沒有被銷毀,就會(huì)收到廣播叉庐。如果是使用 ApplicationContext 進(jìn)行注冊(cè)舒帮,只要程序在運(yùn)行就可以收到廣播。

    (3)停止接收廣播時(shí),可以調(diào)用 unregisterReceiver( android.content.BroadcastReceiver ) 進(jìn)行注銷玩郊。當(dāng)你不需要這個(gè)廣播時(shí)或者 context 不再有效時(shí)肢执,一定要注銷接收器⊥咭耍·

    注意你在哪里注冊(cè)和注銷的接收者蔚万,比如,你在 Activity 的 onCreate( Bundle ) 中注冊(cè)一個(gè)接收者临庇,則應(yīng)該在 onDestory() 中注銷它反璃,防止接收器泄露;如果你在 onResume() 中注冊(cè)一個(gè)接收器假夺,你應(yīng)該在 onPause() 中注銷淮蜈,以防止多次注冊(cè)( 如果你不想在 Activity 暫停時(shí)接收廣播,可以做減少不必要的系統(tǒng)開銷 )已卷。不要在 onSaveInstanceState( Bundle ) 中注銷梧田,如果用戶在歷史棧中移回,則不會(huì)調(diào)用這個(gè)方法侧蘸。

  2. 進(jìn)程狀態(tài)的影響

BroadcastReceiver 的狀態(tài)(無論是否在運(yùn)行)會(huì)影響其包含進(jìn)程的狀態(tài)裁眯,這也可能反過來影響其被系統(tǒng)殺死的可能性。例如讳癌,當(dāng)進(jìn)程執(zhí)行一個(gè)接收器時(shí)(在其 onReceive() 方法中運(yùn)行代碼)穿稳,它被認(rèn)為是一個(gè)前臺(tái)進(jìn)程,除非內(nèi)存壓力極度大的時(shí)候晌坤,否則系統(tǒng)將保持其進(jìn)程的運(yùn)行逢艘。

然而,一旦你的代碼從 onREceive() 方法中返回, BroadcastReceiver 就不在活動(dòng)了骤菠。接收器的宿主進(jìn)程變得和運(yùn)行在這個(gè)進(jìn)程中的其它應(yīng)用程序組件一樣重要它改。如果該進(jìn)程只承載一個(gè) manifest-declared 接收器(應(yīng)用程序從來沒有或者最近沒有跟用戶進(jìn)行交互),然后從 onReceive() 返回時(shí)商乎,系統(tǒng)將認(rèn)為它的進(jìn)程是一個(gè)低優(yōu)先級(jí)的進(jìn)程央拖,很可能會(huì)殺死它,從而施放資源鹉戚,用于其它優(yōu)先級(jí)高的進(jìn)程鲜戒。

因此,不應(yīng)該在廣播接收器中執(zhí)行耗時(shí)的后臺(tái)線程崩瓤,在執(zhí)行 onReceive() 后,系統(tǒng)隨時(shí)可以殺死進(jìn)程以回收內(nèi)存踩官,這樣做會(huì)終止在該進(jìn)程中運(yùn)行生成的線程却桶。為了避免這種情況的發(fā)生,應(yīng)該調(diào)用 goAsync() 方法(如果你需要更多的時(shí)間來處理后臺(tái)線程中的廣播)或使用 JobScheduler 從接收器調(diào)度 JobService ,則系統(tǒng)會(huì)知道該進(jìn)程繼續(xù)執(zhí)行工作颖系。

下面的這段代碼顯示了一個(gè) BroadcastReceiver 使用 goAsync() 來標(biāo)記嗅剖,需要更多的時(shí)間來完成當(dāng)前的操作,并在完成后再 onReceiver() 嘁扼。如果你在 onReceive() 中需要的時(shí)間很長(zhǎng)信粮,導(dǎo)致 UI 線程錯(cuò)過了一幀( 16 ms ),可以使用上面的方法趁啸,在后臺(tái)線程繼續(xù)工作强缘。

public class DemoBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "DemoBroadcastReceiver";

    @Override
    public void onReceive(Context context, final Intent intent) {
        final PendingResult pendingResult = goAsync();

        AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
            @Override
            protected String doInBackground(String... params) {
                StringBuilder sb = new StringBuilder();
                sb.append("Action: " + intent.getAction() + "\n");
                sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
                String log = sb.toString();
                Log.d(TAG, log);

                // 必須調(diào)用 finish() ,以便 BroadcastReceiver 可以被回收不傅。
                pendingResult.finish();

                return log;
            }
        };

        asyncTask.execute();
    }

}

發(fā)送廣播

Android 提供了三種方法供應(yīng)用發(fā)送廣播:

  • sendOrderedBroadcast( Intent , String ) 方法將廣播發(fā)送到一個(gè)接收器旅掂。隨著每個(gè)接收器依次執(zhí)行,它可以將結(jié)果傳遞到下一個(gè)接收器访娶,也可以完全中止廣播商虐,使它不會(huì)傳給其他接收器。Android 可以控制運(yùn)行的命令接收器:匹配 intent-filter 的優(yōu)先級(jí)屬性崖疤;具備相同優(yōu)先級(jí)的接收器秘车,可以任意順序運(yùn)行

  • sendBroadcast( Intent ) 方法向所有接收器發(fā)送未確定順序的廣播,這被成為普通廣播劫哼。這種廣播更有效率叮趴,但是意味著接收器不能讀取來自其他接收器的結(jié)果,傳播從廣播接收的數(shù)據(jù)沦偎,或者中止廣播

  • LocalBroadcastManager.sendBroadcast 方法將廣播發(fā)送到同個(gè)應(yīng)用程序中的接收者疫向。如果你不需要發(fā)送跨應(yīng)用廣播,請(qǐng)使用本地廣播豪嚎。實(shí)施 效率更高(無需進(jìn)行跨進(jìn)程通信)搔驼,不需要擔(dān)心其他可以接收或者發(fā)送廣播的應(yīng)用程序之間的任何安全問題

下面的代碼片段演示了如何通過創(chuàng)建 Intent 并 調(diào)用sendBroadcast( Intent ) 來發(fā)送廣播:

 Intent intent = new Intent();
 intent.setAction("com.passershowe.broadcast.NOTIFICATION_DEMO");
 intent.putExtra("data","Send a notice");
 sendBroadcast(intent);

廣播消息被包裝在 Intent 對(duì)象中,Intent 動(dòng)作字符串必須提供應(yīng)用程序的 java 包名和唯一的廣播事件標(biāo)識(shí)侈询。你可以使用 putExtra( String , Bundle ) 附加信息到 Intent 中舌涨,還可以通過 Intent 調(diào)用 setPackage( String ) 將廣播限制在同一組織中的一組應(yīng)用程序。

注意:雖然 Intent 用于發(fā)送廣播和使用 startActivity( Intent ) 啟動(dòng) Activity 扔字,但這些行為是完全無關(guān)的囊嘉。廣播接收器無法看到或者捕獲用于開始 Activity 的 Intent ;同樣革为,當(dāng)你是廣播 Intent 時(shí)扭粱,是無法找到或啟動(dòng) Activity 。

廣播權(quán)限約束

Permissions(權(quán)限) 允許你將廣播限制為持有一定權(quán)限的應(yīng)用程序集震檩∽粮颍可以對(duì)廣播的發(fā)送方或接收方強(qiáng)制執(zhí)行限制蜓堕。

  1. 發(fā)送權(quán)限
    當(dāng)你調(diào)用 sendBroadcast( Intent , String ) 或 sendOrderedBroadcast( Intent , String , BroadcastReceiver , Handle , int , String , Bundle ) 時(shí),可以指定權(quán)限參數(shù)博其。只有通過其清單中的標(biāo)簽請(qǐng)求許可的接收器才能接收廣播套才。例如,以下代碼發(fā)送廣播:
    sendBroadcast(new Intent("com.passershowe.broadcast.NOTIFY"),      Manifest.permission.SEND_SMS);
    

要接收廣播慕淡,接收應(yīng)用程序必須請(qǐng)求如下所示的權(quán)限:

<uses-permission android:name="android.permission.SEND_SMS"/>

你可以指定一個(gè)現(xiàn)有的系統(tǒng)權(quán)限(如SEND_SMS)或使用 <permission>元素定義自定義權(quán)限

注意:當(dāng)你的應(yīng)用程序注冊(cè)了自定義權(quán)限背伴,你必須在使用這個(gè)權(quán)限前舅踪,安裝它

  1. 接收權(quán)限
    如果你在注冊(cè)廣播接收器(或者使用 registerReceiver( BroadcastReceiver , IntentFilter , String , Handle ) 或清單中的 <receiver> 標(biāo)簽)中指定權(quán)限參數(shù)经宏,則只有使用 <uses-permission> 標(biāo)簽在其清單中(隨后被授予許可是危險(xiǎn)的)可以向接收者發(fā)送 Intent

    例如,假設(shè)你的接收應(yīng)用程序具有清單聲明的接收器域庇,如下所示:

    <receiver android:name=".DemoBroadcastReceiver"
                   android:permission="android.permission.SEND_SMS">
            <intent-filter>
                <action android:name="android.intent.action.AIRPLANE_MODE"/>
            </intent-filter>
    </receiver>
    

    或者你的接收應(yīng)用程序具有 context 注冊(cè)的接收器儿普,如下所示:

    IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
    

    然后崎逃,為了能夠向這些接收者發(fā)送廣播,發(fā)送應(yīng)用程序必須如下所示請(qǐng)求許可:

    <uses-permission android:name="android.permission.SEND_SMS"/>
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眉孩,一起剝皮案震驚了整個(gè)濱河市个绍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浪汪,老刑警劉巖巴柿,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異死遭,居然都是意外死亡广恢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門呀潭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钉迷,“玉大人,你說我怎么就攤上這事钠署】反希” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵谐鼎,是天一觀的道長(zhǎng)舰蟆。 經(jīng)常有香客問我,道長(zhǎng)狸棍,這世上最難降的妖魔是什么身害? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮草戈,結(jié)果婚禮上塌鸯,老公的妹妹穿的比我還像新娘。我一直安慰自己唐片,他們只是感情好丙猬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布丢习。 她就那樣靜靜地躺著,像睡著了一般淮悼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揽思,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天袜腥,我揣著相機(jī)與錄音,去河邊找鬼钉汗。 笑死羹令,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的损痰。 我是一名探鬼主播福侈,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼卢未!你這毒婦竟也來了肪凛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辽社,失蹤者是張志新(化名)和其女友劉穎伟墙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滴铅,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戳葵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汉匙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拱烁。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖噩翠,靈堂內(nèi)的尸體忽然破棺而出戏自,到底是詐尸還是另有隱情,我是刑警寧澤绎秒,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布浦妄,位于F島的核電站,受9級(jí)特大地震影響见芹,放射性物質(zhì)發(fā)生泄漏剂娄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一玄呛、第九天 我趴在偏房一處隱蔽的房頂上張望阅懦。 院中可真熱鬧,春花似錦徘铝、人聲如沸耳胎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怕午。三九已至废登,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郁惜,已是汗流浹背堡距。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兆蕉,地道東北人羽戒。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像虎韵,于是被迫代替她去往敵國(guó)和親易稠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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