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)閉;