一. 介紹
廣播,是一個(gè)全局的監(jiān)聽(tīng)器,屬于Android四大組件之一. 主要用于監(jiān)聽(tīng) / 接收 應(yīng)用 App 發(fā)出的廣播消息,并 做出響應(yīng).
應(yīng)用場(chǎng)景有:
-
Android不同組件間的通信(含 :應(yīng)用內(nèi) / 不同應(yīng)用之間) - 多線程通信
- 與
Android系統(tǒng)在特定情況下的通信
二. 分類
廣播被分為兩種不同的類型:“普通廣播(Normal broadcasts)”和“有序廣播(Ordered broadcasts)”。普通廣播是完全異步的,可以在同一時(shí)刻(邏輯上)被所有接收者接收到,消息傳遞的效率比較高,但缺點(diǎn)是:接收者不能將處理結(jié)果傳遞給下一個(gè)接收者,并且無(wú)法終止廣播Intent的傳播;然而有序廣播是按照接收者聲明的優(yōu)先級(jí)別(聲明在intent-filter元素的android:priority屬性中,數(shù)越大優(yōu)先級(jí)別越高,取值范圍:-1000到1000。也可以調(diào)用IntentFilter對(duì)象的setPriority()進(jìn)行設(shè)置),被接收者依次接收廣播。如:A的級(jí)別高于B,B的級(jí)別高于C,那么,廣播先傳給A,再傳給B,最后傳給C。A得到廣播后,可以往廣播里存入數(shù)據(jù),當(dāng)廣播傳給B時(shí),B可以從廣播中得到A存入的數(shù)據(jù)。
靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)的區(qū)別:

1. 靜態(tài)注冊(cè)(8.0以后無(wú)法使用)
1.無(wú)序廣播,開(kāi)發(fā)者自身定義 intent的廣播(最常用)
-
發(fā)送方式,顯示隱式都可以
public void normalListener(View view) { // Intent intent = new Intent(this, NormalBroadCaseReceiver.class); // intent.putExtra("key","broad"); // sendBroadcast(intent); //隱式啟動(dòng),如果有多個(gè)靜態(tài)注冊(cè)的廣播action 相同,都會(huì)收到 // Intent intent1 = new Intent(); // intent1.setAction("com.kiwilss.broadcase1"); // intent1.putExtra("key", "broad"); // sendBroadcast(intent1); //8.0以后,想發(fā)送成功就要加上 intent1.setPackage(getPackageName()); //原因:谷歌在8.0后為了提高效率,刪除了靜態(tài)注冊(cè),防止關(guān)閉App后廣播還在, // 造成內(nèi)存泄漏, 現(xiàn)在靜態(tài)注冊(cè)的廣播需要指定包名,而動(dòng)態(tài)注冊(cè)就沒(méi)有這個(gè)問(wèn)題 Intent intent1 = new Intent(); intent1.setAction("com.kiwilss.broadcase1"); intent1.putExtra("key", "broad"); intent1.setPackage(getPackageName()); sendBroadcast(intent1); } -
自定義廣播
NormalBroadCaseReceiver
-
public class NormalBroadCaseReceiver extends BroadcastReceiver { @SuppressLint("UnsafeProtectedBroadcastReceiver") @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "onReceive: "+intent.getStringExtra("key") ); } }NormalBroadCaseReceiver2
public class NormalBroadCaseReceiver2 extends BroadcastReceiver { @SuppressLint("UnsafeProtectedBroadcastReceiver") @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "normal2-------onReceive: "+intent.getStringExtra("key") ); } } -
AndroidManifest.xml里注冊(cè)
- 屬性說(shuō)明:
<receiver android:enabled=["true" | "false"] //此broadcastReceiver能否接收其他App的發(fā)出的廣播 //默認(rèn)值是由receiver中有無(wú)intent-filter決定的:如果有intent-filter,默認(rèn)值為true,否則為false android:exported=["true" | "false"] android:icon="drawable resource" android:label="string resource" //繼承BroadcastReceiver子類的類名 android:name=".mBroadcastReceiver" //具有相應(yīng)權(quán)限的廣播發(fā)送者發(fā)送的廣播才能被此BroadcastReceiver所接收; android:permission="string" //BroadcastReceiver運(yùn)行所處的進(jìn)程 //默認(rèn)為app的進(jìn)程,可以指定獨(dú)立的進(jìn)程 //注:Android四大基本組件都可以通過(guò)此屬性指定自己的獨(dú)立進(jìn)程 android:process="string" > //用于指定此廣播接收器將接收的廣播類型 //本示例中給出的是用于接收網(wǎng)絡(luò)狀態(tài)改變時(shí)發(fā)出的廣播 <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>- 注冊(cè)示例
<receiver android:name=".broadcastreceiver.NormalBroadCaseReceiver" tools:ignore="ExportedReceiver"> <intent-filter> <action android:name="com.kiwilss.broadcase1"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver> <receiver android:name=".broadcastreceiver.NormalBroadCaseReceiver2" tools:ignore="ExportedReceiver"> <intent-filter> <action android:name="com.kiwilss.broadcase1"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver>當(dāng)此
App首次啟動(dòng)時(shí),系統(tǒng)會(huì)自動(dòng)實(shí)例化mBroadcastReceiver類,并注冊(cè)到系統(tǒng)中。 -
測(cè)試結(jié)果
04-18 15:52:46.360 12305-12305/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: broad
04-18 15:52:46.363 12305-12305/com.kiwilss.lxkj.fourassembly E/MMM: normal2-------onReceive: broad
2.有序廣播,靜態(tài)注冊(cè)
-
發(fā)送方法和無(wú)序廣播類似
public void orderlyListener(View view) { //和無(wú)序廣播類似 Intent intent1 = new Intent(); intent1.setAction("com.kiwilss.broadcase2"); //intent1.putExtra("key","broad"); intent1.putExtra("key", "orderly"); //null 表示沒(méi)有權(quán)限限制 sendOrderedBroadcast(intent1, null); } -
自定義廣播, 有序廣播可以中斷傳遞, 也可以新增傳遞信息
OrderlyBroadcastReceiver
public class OrderlyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "onReceive: "+intent.getStringExtra("key")); if (TextUtils.equals("orderly",intent.getStringExtra("key"))) { //中斷傳遞 abortBroadcast(); }else { //傳遞新的信息給下一個(gè)廣播 Bundle bundle = new Bundle(); bundle.putString("broad","新的信息"); setResultExtras(bundle); } } }OrderlyBroadcastReceiver2
public class OrderlyBroadcastReceiver2 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "onReceive22 : "+intent.getStringExtra("key")); Bundle bundle = getResultExtras(true); String broad = bundle.getString("broad"); Log.e(TAG, "onReceive222 : "+ broad ); } } -
在清單文件中設(shè)定優(yōu)先級(jí)
<!--可以設(shè)置廣播的優(yōu)先級(jí)--> <receiver android:name=".broadcastreceiver.OrderlyBroadcastReceiver" > <intent-filter android:priority="100"> <action android:name="com.kiwilss.broadcase2"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver> <receiver android:name=".broadcastreceiver.OrderlyBroadcastReceiver2" > <intent-filter android:priority="90"> <action android:name="com.kiwilss.broadcase2"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </receiver>
4.測(cè)試結(jié)果
中斷傳播結(jié)果:
04-18 16:23:30.380 15795-15795/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: orderly
新增信息結(jié)果:
04-18 16:30:35.026 16342-16342/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: broad
04-18 16:30:35.074 16342-16342/com.kiwilss.lxkj.fourassembly E/MMM: onReceive22 : broad
04-18 16:30:35.074 16342-16342/com.kiwilss.lxkj.fourassembly E/MMM: onReceive222 : 新的信息
3.動(dòng)態(tài)注冊(cè)
-
發(fā)送方式
/**普通廣播,動(dòng)態(tài)注冊(cè) * @param view */ public void normalDynamicListener(View view) { //動(dòng)態(tài)注冊(cè)廣播發(fā)送消息 Intent intent = new Intent("com.kiwilss.normaldynamic"); intent.putExtra("key","普通廣播動(dòng)態(tài)注冊(cè)"); sendBroadcast(intent); } -
注冊(cè)和解除注冊(cè)
@Override protected void onResume() { super.onResume(); //普通廣播注冊(cè) if (mBroadcastReceiver == null){ mBroadcastReceiver = new LocalBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter("com.kiwilss.normaldynamic"); registerReceiver(mBroadcastReceiver,intentFilter); } } @Override protected void onPause() { super.onPause(); //普通廣播解除注冊(cè) if (mBroadcastReceiver != null) { unregisterReceiver(mBroadcastReceiver); } }
- 動(dòng)態(tài)廣播最好在
Activity的onResume()注冊(cè)、onPause()注銷。 - 原因:
- 對(duì)于動(dòng)態(tài)廣播,有注冊(cè)就必然得有注銷,否則會(huì)導(dǎo)致內(nèi)存泄露
- 重復(fù)注冊(cè)、重復(fù)注銷也不允許
自定義廣播
class LocalBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e("MMM", "onReceive: " + intent.getStringExtra("key")); } }-
測(cè)試結(jié)果
04-18 16:52:14.730 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 普通廣播動(dòng)態(tài)注冊(cè)
4. 本地廣播 App應(yīng)用內(nèi)廣播(Local Broadcast)
- Android中的廣播可以跨App直接通信(exported對(duì)于有intent-filter情況下默認(rèn)值為true)
可能出現(xiàn)的問(wèn)題:
其他App針對(duì)性發(fā)出與當(dāng)前App intent-filter相匹配的廣播,由此導(dǎo)致當(dāng)前App不斷接收廣播并處理;
其他App注冊(cè)與當(dāng)前App一致的intent-filter用于接收廣播,獲取廣播具體信息;
即會(huì)出現(xiàn)安全性 & 效率性的問(wèn)題。-
解決方案
使用App應(yīng)用內(nèi)廣播(Local Broadcast)App應(yīng)用內(nèi)廣播可理解為一種局部廣播,廣播的發(fā)送者和接收者都同屬于一個(gè)App。
相比于全局廣播(普通廣播),App應(yīng)用內(nèi)廣播優(yōu)勢(shì)體現(xiàn)在:安全性高 & 效率高
-
具體使用
發(fā)送方法:
/** * 對(duì)于LocalBroadcastManager方式發(fā)送的應(yīng)用內(nèi)廣播, * 只能通過(guò)LocalBroadcastManager動(dòng)態(tài)注冊(cè),不能靜態(tài)注冊(cè) * * @param view */ public void appListener(View view) { //注冊(cè)應(yīng)用內(nèi)廣播接收器 //步驟1:實(shí)例化BroadcastReceiver子類 & IntentFilter mBroadcastReceiver mBroadcastReceiver = new LocalBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); //步驟2:實(shí)例化LocalBroadcastManager的實(shí)例 localBroadcastManager = LocalBroadcastManager.getInstance(this); //步驟3:設(shè)置接收廣播的類型 intentFilter.addAction("com.kiwilss.local"); //4,注冊(cè) localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter); //發(fā)送應(yīng)用內(nèi)廣播測(cè)試 Intent intent = new Intent("com.kiwilss.local"); intent.putExtra("key","本地廣播"); localBroadcastManager.sendBroadcast(intent); } @Override protected void onDestroy() { super.onDestroy(); //取消注冊(cè)本地廣播 if (mBroadcastReceiver != null) localBroadcastManager.unregisterReceiver(mBroadcastReceiver); //取消網(wǎng)絡(luò)監(jiān)聽(tīng)注冊(cè) if (netBroadcastReceiver != null) unregisterReceiver(netBroadcastReceiver); } 自定義廣播
class LocalBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e("MMM", "onReceive: " + intent.getStringExtra("key")); } }-
測(cè)試結(jié)果
04-18 16:57:28.905 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 本地廣播
5.系統(tǒng)廣播(System Broadcast)
- 介紹
Android中內(nèi)置了多個(gè)系統(tǒng)廣播:只要涉及到手機(jī)的基本操作(如開(kāi)機(jī)、網(wǎng)絡(luò)狀態(tài)變化、拍照等等),都會(huì)發(fā)出相應(yīng)的廣播
每個(gè)廣播都有特定的Intent - Filter(包括具體的action),Android系統(tǒng)廣播action如下:
-
系統(tǒng)操作 action 監(jiān)聽(tīng)網(wǎng)絡(luò)變化 android.net.conn.CONNECTIVITY_CHANGE 關(guān)閉或打開(kāi)飛行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED 充電時(shí)或電量發(fā)生變化 Intent.ACTION_BATTERY_CHANGED 電池電量低 Intent.ACTION_BATTERY_LOW 電池電量充足(即從電量低變化到飽滿時(shí)會(huì)發(fā)出廣播 Intent.ACTION_BATTERY_OKAY 系統(tǒng)啟動(dòng)完成后(僅廣播一次) Intent.ACTION_BOOT_COMPLETED 按下照相時(shí)的拍照按鍵(硬件按鍵)時(shí) Intent.ACTION_CAMERA_BUTTON 屏幕鎖屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS 設(shè)備當(dāng)前設(shè)置被改變時(shí)(界面語(yǔ)言、設(shè)備方向等) Intent.ACTION_CONFIGURATION_CHANGED 插入耳機(jī)時(shí) Intent.ACTION_HEADSET_PLUG 未正確移除SD卡但已取出來(lái)時(shí)(正確移除方法:設(shè)置--SD卡和設(shè)備內(nèi)存--卸載SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL 插入外部?jī)?chǔ)存裝置(如SD卡) Intent.ACTION_MEDIA_CHECKING 成功安裝APK Intent.ACTION_PACKAGE_ADDED 成功刪除APK Intent.ACTION_PACKAGE_REMOVED 重啟設(shè)備 Intent.ACTION_REBOOT 屏幕被關(guān)閉 Intent.ACTION_SCREEN_OFF 屏幕被打開(kāi) Intent.ACTION_SCREEN_ON 關(guān)閉系統(tǒng)時(shí) Intent.ACTION_SHUTDOWN 重啟設(shè)備 Intent.ACTION_REBOOT 注:當(dāng)使用系統(tǒng)廣播時(shí),只需要在注冊(cè)廣播接收者時(shí)定義相關(guān)的action即可,并不需要手動(dòng)發(fā)送廣播,當(dāng)系統(tǒng)有相關(guān)操作時(shí)會(huì)自動(dòng)進(jìn)行系統(tǒng)廣播
-
使用示例, 監(jiān)聽(tīng)手機(jī)網(wǎng)絡(luò)狀態(tài)
-
方法
/**系統(tǒng)廣播,示例監(jiān)聽(tīng)網(wǎng)絡(luò) * @param view */ NetBroadcastReceiver netBroadcastReceiver; public void appDynamicListener(View view) { netBroadcastReceiver = new NetBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); registerReceiver(netBroadcastReceiver,intentFilter); //ondestory中取消注冊(cè) } -
自定義廣播
class NetBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Log.e("MMM", "onReceive: 網(wǎng)絡(luò)有變化" ); //連接或是關(guān)閉網(wǎng)絡(luò)時(shí)可以監(jiān)控 } } -
測(cè)試, 關(guān)閉打開(kāi)手機(jī)網(wǎng)絡(luò)
04-18 17:02:03.220 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 網(wǎng)絡(luò)有變化
04-18 17:02:06.834 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 網(wǎng)絡(luò)有變化
04-18 17:02:13.506 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 網(wǎng)絡(luò)有變化
-
三.動(dòng)態(tài)注冊(cè)幫助類
鑒于各種限制,盡量使用動(dòng)態(tài)注冊(cè),所以寫(xiě)了一個(gè)幫助類,方便快速注冊(cè)和解除注冊(cè)廣播。比較簡(jiǎn)單,代碼如下:
object BroadcastKtx {
/**
* 注冊(cè)廣播
*
* @param context
* @param broadcastReceiver
* @param action
*/
fun registerBroadcast(
context: Context?,
broadcastReceiver: BroadcastReceiver?,
vararg action: String?
) {
if (context == null || broadcastReceiver == null) return
val intentFilter = IntentFilter()
for (element in action) {
intentFilter.addAction(element)
}
context.registerReceiver(broadcastReceiver, intentFilter)
}
/**
* 解除注冊(cè)廣播,廣播要和注冊(cè)時(shí)是同一個(gè)
*
* @param context
* @param broadcastReceiver
*/
fun unregisterBroadcast(context: Context?, broadcastReceiver: BroadcastReceiver?) {
if (context == null || broadcastReceiver == null) return
context.unregisterReceiver(broadcastReceiver)
}
}
/**
* 注冊(cè)廣播
*
* @param broadcastReceiver
* @param action
*/
fun Context?.registerBroadcast(
broadcastReceiver: BroadcastReceiver?,
vararg action: String?
) = BroadcastKtx.registerBroadcast(this, broadcastReceiver, *action)
/**
* 解除注冊(cè)廣播,廣播要和注冊(cè)時(shí)是同一個(gè)
*
* @param broadcastReceiver
*/
fun Context?.unregisterBroadcast(broadcastReceiver: BroadcastReceiver?) =
BroadcastKtx.unregisterBroadcast(this, broadcastReceiver)
/**
* 注冊(cè)廣播
*
* @param broadcastReceiver
* @param action
*/
fun Fragment?.registerBroadcast(
broadcastReceiver: BroadcastReceiver?,
vararg action: String?
) = BroadcastKtx.registerBroadcast(this?.context, broadcastReceiver, *action)
/**
* 解除注冊(cè)廣播,廣播要和注冊(cè)時(shí)是同一個(gè)
*
* @param broadcastReceiver
*/
fun Fragment?.unregisterBroadcast(broadcastReceiver: BroadcastReceiver?) =
BroadcastKtx.unregisterBroadcast(this?.context, broadcastReceiver)
使用很簡(jiǎn)單,在 Activity/Fragment 中直接使用,示例 demo如下:
class BroadcastActivity: AppCompatActivity(R.layout.activity_broadcast) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
btnSend.setOnClickListener {
//發(fā)送廣播信息
sendBroadcast(createIntentBroadcast(action1,"broad" to "test broadcast"))
}
}
//初始化廣播
val mTestBroadcast by lazy { TestBroadcast() }
val action1 = "com.kiwilss.broadcase1"
override fun onResume() {
super.onResume()
//注冊(cè),可以注冊(cè)很多個(gè) action
registerBroadcast(mTestBroadcast,action1)
}
override fun onPause() {
super.onPause()
unregisterBroadcast(mTestBroadcast)
}
}
class TestBroadcast: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action
Log.e("MMM", "onReceive: $action --- ${intent?.getStringExtra("broad")}");
}
}