前言:
最近的計劃是學習一下iOS的NSNotificationCenter,突然想起來的Android的廣播機制,所以還是覺得先對BroadcastReceiver來個全面的總結然後再去學習NSNotificationCenter。
BroadcastReceiver簡介:
BroadcastReceiver是Android四大元件之一,廣播是一種廣泛運用的在應用程式之間傳輸資訊的機制,而BroadcastReceiver 是對傳送出來的廣播進行過濾接收並響應的一類元件;廣播接收者( BroadcastReceiver )用於接收廣播 Intent ,廣播 Intent 的傳送是通過呼叫 Context.sendBroadcast() 、 Context.sendOrderedBroadcast() 來實現的。通常一個廣播 Intent 可以被訂閱了此 Intent 的多個廣播接收者所接收。
廣播的使用場景:
1.同一app內部的同一元件內的訊息通訊(單個或多個執行緒之間);
2.同一app內部的不同元件之間的訊息通訊(單個程式);
3.同一app具有多個程式的不同元件之間的訊息通訊;
4.不同app之間的元件之間訊息通訊;
5.Android系統在特定情況下與App之間的訊息通訊。
廣播的分類:
普通廣播:
傳送方式:Context.sendBroadcast()
優點:完全非同步,訊息傳遞效率高,
缺點:不能處理廣播傳給一個接收者,不能終止廣播的傳播
有序廣播:
傳送方式:Context.sendOrderedBroadcast()
優點:可以根據廣播接收者的優先順序依次傳播,廣播接收者可以處理廣播然後再傳給一下廣播接收者,也可以根據需要呼叫abortBroadcast()終止廣播傳播。
缺點:效率低
廣播的使用方式:
動態註冊:
//註冊廣播 private void registerReceiver(){ IntentFilter dynamicFilter = new IntentFilter(); dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//新增動態廣博Action registerReceiver(dynamicReceiver, dynamicFilter); } //解除註冊 private void unRegisterReceiver() { unregisterReceiver(dynamicReceiver); } //動態廣播的Receiver private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(ActionCodes.DYNAMICACTION)){ //動作檢測 String msg = intent.getStringExtra("msg"); String finalMsg= String.format("%s%s","CActivity----->收到廣播:",msg); Log.e("dynamicReceiver",finalMsg); Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show(); } } };
一般情況在Activity/Fragment 的onCreate/onStart/onResume 中註冊, 在onDestory/onStop/onPause 中解除註冊,根據不同的需求選擇不能的生命週期函式。
靜態註冊:
<receiver android:name=".StaticReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
傳送廣播:
//傳送普通廣播 Intent intent = new Intent(); intent.setAction(ActionCodes.DYNAMICACTION);//設定Action intent.putExtra("msg", "我是普通廣播(動態註冊)");//新增附加資訊 sendBroadcast(intent); //傳送有序廣播 Intent intent = new Intent(); intent.setAction(ActionCodes.DYNAMICACTION);//設定Action intent.setPackage(getPackageName());//設定包名 intent.putExtra("msg", "我是有序廣播(動態註冊)");//新增附加資訊 sendOrderedBroadcast(intent,null);
以上基本上可以滿足廣播的基本使用了,接下來我們在寫個測試程式:分別在A,B,C三個Activity中動態註冊廣播,分別傳送普通廣播和和有序廣播。
傳送普通廣播接收順序:
分別給A,B,C三個Activity中動態註冊廣播的優先順序設定未100,500,1000接收順序:
附上設定優先順序方式:
IntentFilter dynamicFilter = new IntentFilter(); dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//新增動態廣播的Action dynamicFilter.setPriority(1000);//設定優先順序 registerReceiver(dynamicReceiver, dynamicFilter);
上文已知有序廣播可以修改廣播資訊傳遞給下一級優先順序低的接收者,我們讓BActivity修改 讓AActivity接收:
BActivity 廣播實現
//動態廣播的Receiver private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(ActionCodes.DYNAMICACTION)){ //動作檢測 String msg = intent.getStringExtra("msg"); String finalMsg= String.format("%s%s","BActivity----->收到廣播:",msg); Log.e("dynamicReceiver",finalMsg); Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show(); if(isOrderedBroadcast()) { //建立一個Bundle物件,並存入資料 Bundle bundle = new Bundle(); bundle.putString("msg", msg + "來自BActivity"); //將bundle放入結果中 setResultExtras(bundle); //取消Broadcast的繼續傳送 //abortBroadcast(); } } } };
AActivity 如何接收:
//動態廣播的Receiver private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(ActionCodes.DYNAMICACTION)) { //動作檢測 String msg = intent.getStringExtra("msg"); String finalMsg = String.format("%s%s", "AActivity----->收到廣播:", msg); Log.e("dynamicReceiver", finalMsg); if (isOrderedBroadcast()) { Bundle bundle = getResultExtras(true);//接收來自上一級優先順序較高的廣播修改的資訊 String from = bundle.getString("msg"); if (TextUtils.isEmpty(from)) { return; } Log.e("dynamicReceiver", String.format("%s%s", "AActivity----->收到廣播:", from)); Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show(); } } } };
執行結果:
有序廣播如何終止廣播傳播:
// 終止Broadcast的繼續傳送 abortBroadcast();
執行結果:
靜態註冊的廣播上述測試執行結果一致,設定優先順序方式不同而已:
<receiver android:name=".AStaticReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"><!--設定優先順序--> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
接下來測試一下app應用之間傳送廣播,傳送方式也是通過隱式Intent方式:
動態註冊廣播接收情況:
普通廣播:
有序廣播:
靜態註冊廣播接收情況:
普通廣播:
有序廣播:
看了上述測試結果基本上和app內執行效果一模一樣,所以按照上述那種註冊方式和使用方式,一旦app被反編譯之後有一定的安全隱患,如何安全的傳輸呢?
第一種方式:
靜態註冊廣播可以設定:android:exported="false"
<receiver android:name=".AStaticReceiver" android:enabled="true" android:exported="false" <!--設定只能接收app內廣播 --> > <intent-filter android:priority="100"><!--設定優先順序--> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
第二種方式:通過設定傳送的廣播只能app內接收
Intent intent = new Intent(); intent.setAction(ActionCodes.DYNAMICACTION);//設定Action intent.setPackage(getPackageName());//設定包名使廣播只能被包名的app內接收者接收 intent.putExtra("msg", "我是普通廣播(動態註冊)");//新增附加資訊 sendBroadcast(intent);
第三種方式通過自定義許可權:通過上述兩種方式只能達到遮蔽外來廣播以及廣播只在app內傳播,無法實現app之間安全傳送廣播
自定義許可權:
<permission android:name="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver" /> <uses-permission android:name="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver"/>
動態註冊:
IntentFilter dynamicFilter = new IntentFilter(); dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//新增動態廣播的Action dynamicFilter.setPriority(500); //設定許可權 registerReceiver(dynamicReceiver, dynamicFilter,ActionCodes.MYPERMISSION,null);
靜態註冊:
<receiver android:name=".BStaticReceiver" android:enabled="true" android:exported="true" <!--設定許可權--> android:permission="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver"> <intent-filter android:priority="500"> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
傳送廣播:
//普通廣播 sendBroadcast(intent,ActionCodes.MYPERMISSION); //有序廣播 sendOrderedBroadcast(intent,ActionCodes.MYPERMISSION);
第四種方式:通過LocalBroadcastManager方式
註冊:
LocalBroadcastManager.getInstance(getInstance()).registerReceiver(receiver, filter);
解除註冊:
LocalBroadcastManager.getInstance(getInstance()).unregisterReceiver(receiver);
傳送廣播:
LocalBroadcastManager.getInstance(getInstance()).sendBroadcastSync(intent);
總結:
通過本文可以看出BroadcastReceiver使用方式雖然看似簡單,想要實現比較完善的廣播還是要費一番功夫的。