一. 介紹
廣播缚柳,是一個全局的監(jiān)聽器,屬于Android
四大組件之一. 主要用于監(jiān)聽 / 接收 應用 App
發(fā)出的廣播消息腮介,并 做出響應.
應用場景有:
-
Android
不同組件間的通信(含 :應用內(nèi) / 不同應用之間) - 多線程通信
- 與
Android
系統(tǒng)在特定情況下的通信
二. 分類
廣播被分為兩種不同的類型:“普通廣播(Normal broadcasts)”和“有序廣播(Ordered broadcasts)”。普通廣播是完全異步的,可以在同一時刻(邏輯上)被所有接收者接收到捧弃,消息傳遞的效率比較高,但缺點是:接收者不能將處理結(jié)果傳遞給下一個接收者擦囊,并且無法終止廣播Intent的傳播违霞;然而有序廣播是按照接收者聲明的優(yōu)先級別(聲明在intent-filter元素的android:priority屬性中,數(shù)越大優(yōu)先級別越高,取值范圍:-1000到1000瞬场。也可以調(diào)用IntentFilter對象的setPriority()進行設(shè)置)买鸽,被接收者依次接收廣播。如:A的級別高于B,B的級別高于C,那么贯被,廣播先傳給A眼五,再傳給B,最后傳給C彤灶。A得到廣播后看幼,可以往廣播里存入數(shù)據(jù),當廣播傳給B時,B可以從廣播中得到A存入的數(shù)據(jù)幌陕。
靜態(tài)注冊和動態(tài)注冊的區(qū)別:
1. 靜態(tài)注冊(8.0以后無法使用)
1.無序廣播,開發(fā)者自身定義 intent
的廣播(最常用)
-
發(fā)送方式,顯示隱式都可以
public void normalListener(View view) { // Intent intent = new Intent(this, NormalBroadCaseReceiver.class); // intent.putExtra("key","broad"); // sendBroadcast(intent); //隱式啟動,如果有多個靜態(tài)注冊的廣播action 相同,都會收到 // 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)注冊,防止關(guān)閉App后廣播還在苞轿, // 造成內(nèi)存泄漏茅诱, 現(xiàn)在靜態(tài)注冊的廣播需要指定包名,而動態(tài)注冊就沒有這個問題 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里注冊
- 屬性說明:
<receiver android:enabled=["true" | "false"] //此broadcastReceiver能否接收其他App的發(fā)出的廣播 //默認值是由receiver中有無intent-filter決定的:如果有intent-filter搬卒,默認值為true瑟俭,否則為false android:exported=["true" | "false"] android:icon="drawable resource" android:label="string resource" //繼承BroadcastReceiver子類的類名 android:name=".mBroadcastReceiver" //具有相應權(quán)限的廣播發(fā)送者發(fā)送的廣播才能被此BroadcastReceiver所接收; android:permission="string" //BroadcastReceiver運行所處的進程 //默認為app的進程契邀,可以指定獨立的進程 //注:Android四大基本組件都可以通過此屬性指定自己的獨立進程 android:process="string" > //用于指定此廣播接收器將接收的廣播類型 //本示例中給出的是用于接收網(wǎng)絡(luò)狀態(tài)改變時發(fā)出的廣播 <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
- 注冊示例
<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>
當此
App
首次啟動時摆寄,系統(tǒng)會自動實例化mBroadcastReceiver
類,并注冊到系統(tǒng)中坯门。 -
測試結(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)注冊
-
發(fā)送方法和無序廣播類似
public void orderlyListener(View view) { //和無序廣播類似 Intent intent1 = new Intent(); intent1.setAction("com.kiwilss.broadcase2"); //intent1.putExtra("key","broad"); intent1.putExtra("key", "orderly"); //null 表示沒有權(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 { //傳遞新的信息給下一個廣播 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)先級
<!--可以設(shè)置廣播的優(yōu)先級--> <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.測試結(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.動態(tài)注冊
-
發(fā)送方式
/**普通廣播,動態(tài)注冊 * @param view */ public void normalDynamicListener(View view) { //動態(tài)注冊廣播發(fā)送消息 Intent intent = new Intent("com.kiwilss.normaldynamic"); intent.putExtra("key","普通廣播動態(tài)注冊"); sendBroadcast(intent); }
-
注冊和解除注冊
@Override protected void onResume() { super.onResume(); //普通廣播注冊 if (mBroadcastReceiver == null){ mBroadcastReceiver = new LocalBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter("com.kiwilss.normaldynamic"); registerReceiver(mBroadcastReceiver,intentFilter); } } @Override protected void onPause() { super.onPause(); //普通廣播解除注冊 if (mBroadcastReceiver != null) { unregisterReceiver(mBroadcastReceiver); } }
- 動態(tài)廣播最好在
Activity
的onResume()
注冊微饥、onPause()
注銷。 - 原因:
- 對于動態(tài)廣播古戴,有注冊就必然得有注銷欠橘,否則會導致內(nèi)存泄露
- 重復注冊、重復注銷也不允許
自定義廣播
class LocalBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e("MMM", "onReceive: " + intent.getStringExtra("key")); } }
-
測試結(jié)果
04-18 16:52:14.730 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 普通廣播動態(tài)注冊
4. 本地廣播 App應用內(nèi)廣播(Local Broadcast)
- Android中的廣播可以跨App直接通信(exported對于有intent-filter情況下默認值為true)
可能出現(xiàn)的問題:
其他App針對性發(fā)出與當前App intent-filter相匹配的廣播现恼,由此導致當前App不斷接收廣播并處理肃续;
其他App注冊與當前App一致的intent-filter用于接收廣播黍檩,獲取廣播具體信息;
即會出現(xiàn)安全性 & 效率性的問題始锚。-
解決方案
使用App應用內(nèi)廣播(Local Broadcast)App應用內(nèi)廣播可理解為一種局部廣播刽酱,廣播的發(fā)送者和接收者都同屬于一個App。
相比于全局廣播(普通廣播)瞧捌,App應用內(nèi)廣播優(yōu)勢體現(xiàn)在:安全性高 & 效率高
-
具體使用
發(fā)送方法:
/** * 對于LocalBroadcastManager方式發(fā)送的應用內(nèi)廣播棵里, * 只能通過LocalBroadcastManager動態(tài)注冊,不能靜態(tài)注冊 * * @param view */ public void appListener(View view) { //注冊應用內(nèi)廣播接收器 //步驟1:實例化BroadcastReceiver子類 & IntentFilter mBroadcastReceiver mBroadcastReceiver = new LocalBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); //步驟2:實例化LocalBroadcastManager的實例 localBroadcastManager = LocalBroadcastManager.getInstance(this); //步驟3:設(shè)置接收廣播的類型 intentFilter.addAction("com.kiwilss.local"); //4,注冊 localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter); //發(fā)送應用內(nèi)廣播測試 Intent intent = new Intent("com.kiwilss.local"); intent.putExtra("key","本地廣播"); localBroadcastManager.sendBroadcast(intent); } @Override protected void onDestroy() { super.onDestroy(); //取消注冊本地廣播 if (mBroadcastReceiver != null) localBroadcastManager.unregisterReceiver(mBroadcastReceiver); //取消網(wǎng)絡(luò)監(jiān)聽注冊 if (netBroadcastReceiver != null) unregisterReceiver(netBroadcastReceiver); }
自定義廣播
class LocalBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.e("MMM", "onReceive: " + intent.getStringExtra("key")); } }
-
測試結(jié)果
04-18 16:57:28.905 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 本地廣播
5.系統(tǒng)廣播(System Broadcast)
- 介紹
Android中內(nèi)置了多個系統(tǒng)廣播:只要涉及到手機的基本操作(如開機姐呐、網(wǎng)絡(luò)狀態(tài)變化殿怜、拍照等等),都會發(fā)出相應的廣播
每個廣播都有特定的Intent - Filter(包括具體的action)曙砂,Android系統(tǒng)廣播action如下:
-
系統(tǒng)操作 action 監(jiān)聽網(wǎng)絡(luò)變化 android.net.conn.CONNECTIVITY_CHANGE 關(guān)閉或打開飛行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED 充電時或電量發(fā)生變化 Intent.ACTION_BATTERY_CHANGED 電池電量低 Intent.ACTION_BATTERY_LOW 電池電量充足(即從電量低變化到飽滿時會發(fā)出廣播 Intent.ACTION_BATTERY_OKAY 系統(tǒng)啟動完成后(僅廣播一次) Intent.ACTION_BOOT_COMPLETED 按下照相時的拍照按鍵(硬件按鍵)時 Intent.ACTION_CAMERA_BUTTON 屏幕鎖屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS 設(shè)備當前設(shè)置被改變時(界面語言稳捆、設(shè)備方向等) Intent.ACTION_CONFIGURATION_CHANGED 插入耳機時 Intent.ACTION_HEADSET_PLUG 未正確移除SD卡但已取出來時(正確移除方法:設(shè)置--SD卡和設(shè)備內(nèi)存--卸載SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL 插入外部儲存裝置(如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 屏幕被打開 Intent.ACTION_SCREEN_ON 關(guān)閉系統(tǒng)時 Intent.ACTION_SHUTDOWN 重啟設(shè)備 Intent.ACTION_REBOOT 注:當使用系統(tǒng)廣播時,只需要在注冊廣播接收者時定義相關(guān)的action即可麦轰,并不需要手動發(fā)送廣播,當系統(tǒng)有相關(guān)操作時會自動進行系統(tǒng)廣播
-
使用示例, 監(jiān)聽手機網(wǎng)絡(luò)狀態(tài)
-
方法
/**系統(tǒng)廣播,示例監(jiān)聽網(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中取消注冊 }
-
自定義廣播
class NetBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Log.e("MMM", "onReceive: 網(wǎng)絡(luò)有變化" ); //連接或是關(guān)閉網(wǎng)絡(luò)時可以監(jiān)控 } }
-
測試, 關(guān)閉打開手機網(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ò)有變化
-
三.動態(tài)注冊幫助類
鑒于各種限制砖织,盡量使用動態(tài)注冊款侵,所以寫了一個幫助類,方便快速注冊和解除注冊廣播侧纯。比較簡單新锈,代碼如下:
object BroadcastKtx {
/**
* 注冊廣播
*
* @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)
}
/**
* 解除注冊廣播,廣播要和注冊時是同一個
*
* @param context
* @param broadcastReceiver
*/
fun unregisterBroadcast(context: Context?, broadcastReceiver: BroadcastReceiver?) {
if (context == null || broadcastReceiver == null) return
context.unregisterReceiver(broadcastReceiver)
}
}
/**
* 注冊廣播
*
* @param broadcastReceiver
* @param action
*/
fun Context?.registerBroadcast(
broadcastReceiver: BroadcastReceiver?,
vararg action: String?
) = BroadcastKtx.registerBroadcast(this, broadcastReceiver, *action)
/**
* 解除注冊廣播眶熬,廣播要和注冊時是同一個
*
* @param broadcastReceiver
*/
fun Context?.unregisterBroadcast(broadcastReceiver: BroadcastReceiver?) =
BroadcastKtx.unregisterBroadcast(this, broadcastReceiver)
/**
* 注冊廣播
*
* @param broadcastReceiver
* @param action
*/
fun Fragment?.registerBroadcast(
broadcastReceiver: BroadcastReceiver?,
vararg action: String?
) = BroadcastKtx.registerBroadcast(this?.context, broadcastReceiver, *action)
/**
* 解除注冊廣播妹笆,廣播要和注冊時是同一個
*
* @param broadcastReceiver
*/
fun Fragment?.unregisterBroadcast(broadcastReceiver: BroadcastReceiver?) =
BroadcastKtx.unregisterBroadcast(this?.context, broadcastReceiver)
使用很簡單,在 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()
//注冊,可以注冊很多個 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")}");
}
}