第六章 組件通訊與廣播消息( 普通廣播、有序廣播、粘性廣播、本地廣播

?參考資料:
《Android應(yīng)用程序開發(fā)》ISBN 9787302283164
參考軟件:
Android Studio、Eclipse+ADT、Android SDK、JDK
普通廣播
普通廣播是完全異步執(zhí)行的廣播。該廣播沒有任何的順序可言,因此效率比較高。也意味著它無法被截斷。廣播的工作流程如下:?

?
廣播之間不能傳遞數(shù)據(jù),不能終止廣播
這是一種不需要考慮接收者接收順序的廣播,比如說有3個接收機,都關(guān)注custom.action.mybroadcast這種廣播,無所謂誰先收到誰后收到。接收機不能阻止其它接收機獲取到這條廣播。

(1)、我們創(chuàng)建一個名為GostBroadcast的類繼承BroadcastReceiver,實現(xiàn)一個自定義的廣播。
?


??public?class?GostBroadcast?extends?BroadcastReceiver?{??
??????@Override??
??????public?void?onReceive(Context?context,?Intent?intent)?{??
??????????String?data?=?intent.getStringExtra("data");??
??????????Toast.makeText(context,?data?+?"/cast",?Toast.LENGTH_LONG).show();??
??????}??
??}??
(2)、注冊廣播,我們注冊一個應(yīng)用程序級別的廣播,在manifest文件中進行注冊。注意在注冊廣播的時候需要指定該廣播的action
??<receiver?android:name="com.dsw.servicedemo.GostBroadcast"??
??????????????android:exported="false">??
??????????????<intent-filter>??
??????????????????<action?android:name="com.dsw.send"/>??
??????????????</intent-filter>??
??</receiver>??
(3)、發(fā)送廣播,通過Intent進行廣播的啟動發(fā)送。
??Intent?intent?=?new?Intent(); ?
??intent.setAction("com.dsw.send");??
??intent.putExtra("data",?"data");??
??sendBroadcast(intent);??
通過這樣一個流程,我們就完成了一個廣播從創(chuàng)建、發(fā)送、接收處理的整個過程。最后在頁面彈出Toast展示我們傳遞的數(shù)據(jù)。
有序廣播
有序廣播是同步執(zhí)行的,廣播可以被中斷。??
有序廣播是用sendOrderedBroadcast來發(fā)送。高優(yōu)先級的接收者會先接收到廣播,然后它可以決定是否繼續(xù)轉(zhuǎn)發(fā),讓低優(yōu)先級的接收者接收到,或者終止廣播。高優(yōu)先級的接收者可以通過setResult把一些信息傳給下一個接收者,下一個接收者則通過getResult獲取上一個接收者傳過來的信息。這個優(yōu)先級也是用android:priority來設(shè)置,范圍是-1000到1000。

這是一種需要考慮接收者接收順序的廣播,比如說有3個接收機,都關(guān)注custom.action.mybroadcast這種廣播,那么安卓系統(tǒng)將根據(jù)這3個接收機聲明的優(yōu)先級進行廣播的投遞。
而且有序廣播是可以被阻截的。
比如,一個廣播按照順序傳遞給3個接收機-A B C,但是B將廣播攔截了,因此C將不會收到這個廣播。

1?發(fā)送
Intent?i?=?new?Intent("custom.action.mybroadcast");
sendOrderedBroadcast(i,?null);
2?接收
接收的時候,需要給intent-filter標(biāo)簽設(shè)置android:priority屬性,表示這個接收機的優(yōu)先級。優(yōu)先級從-1000到1000,數(shù)值越大,優(yōu)先級越高。
<receiver
???android:name=".MyReceiver"
???android:enabled="true"
???android:exported="true">
???<intent-filterandroid:priority="1000">
???????<actionandroid:name="custom.action.mybroadcast"/>
???????<categoryandroid:name="android.intent.category.DEFAULT"?/>
???</intent-filter>
</receiver>
接收到廣播以后,Broadcast Receiver可以將廣播攔截,禁止它往下傳播,
publicclassMyReceiverextendsBroadcastReceiver?{
???publicMyReceiver()?{
???}
???//實現(xiàn)onReceive接口
???@Override
???publicvoidonReceive(Context?context,?Intent?intent)?{
???????//禁止往下傳播
???????abortBroadcast();
???}
}
如果接收機1在onReceive()中,希望把數(shù)據(jù)傳遞給下個接收機2,
接收機1可以使用setResultExtras()方法,
publicclassMyReceiver1extendsBroadcastReceiver?{
????publicMyReceiver()?{
????}
????@Override
????publicvoidonReceive(Context?context,?Intent?intent)?{
????????Bundle?b?=?new?Bundle();
????????b.putString("data",?"this?data?from?MyReceiver");
????????setResultExtras(b);
????}
}
在接收機2中,
publicclassMyReceiver2extendsBroadcastReceiver?{
????publicMyReceiver2()?{
????}
????@Override
????publicvoidonReceive(Context?context,?Intent?intent)?{
????????Bundle?b?=?getResultExtras(true);
????????if(b!=null)
????????{
????????????//data就是前一個接收機1傳來的-this?data?from?MyReceiver
????????????String?data?=?b.getString("data");
????????}
????}
}
假如希望將數(shù)據(jù)放到onReceive()傳入的Intent當(dāng)中,是不會傳遞成功的,
@Override
publicvoidonReceive(Context?context,?Intent?intent)?{
????//這是不會成功的
????intent.putExtra("data",?"this?data?from?MyReceiver");
}
傳遞數(shù)據(jù),一定要通過BroadcastReceiver提供的setResultExtras()方法。
粘性廣播
在Android系統(tǒng)粘性廣播一般用來確保重要的狀態(tài)改變后的信息被持久保存,并且能隨時廣播給新的廣播接收器,比如電源的改變,因為耗電需要一個過程,前一個過程必須提前得到,否則可能遇到下次剛好接收到的廣播后系統(tǒng)自動關(guān)機了,隨之而來的是kill行為,所以對某些未處理完的任務(wù)來說,后果很嚴重。
發(fā)送粘性廣播需要權(quán)限(這里的權(quán)限是保存信息的權(quán)限和由系統(tǒng)發(fā)送未處理的廣播的權(quán)限)
<uses-permission?android:name="android.permission.BROADCAST_STICKY"?/>
對于粘性廣播的發(fā)送,和普通廣播的發(fā)送方式是一致的。為了測試粘性廣播的正確使用方式,我們需要定義一個SenderActivity來發(fā)送stick Broadcast
public?class?SenderActivity{@Overrideprotected?void?onCreate(Bundle?saveInstance)?{????super.onCreate(saveInstance);????setContentView(R.layout.stcik_test_layout);????getWindow().setBackgroundDrawableResource(R.drawable.avatar11);}@Overrideprotected?void?onResume()?{????super.onResume();getWindow().getDecorView().postDelayed(new?Runnable()?{????@Override????????public?void?run()?{?????sendStickyBroadcast();??????}????},?3*1000);}private?void?sendStickyBroadcast(){????Intent?i?=?new?Intent();????i.setAction(StickyBroadcastReceiver.Action);????i.putExtra("info",?"sticky?broadcast?has?been?receiver");????sendStickyBroadcast(i);????Log.i("Other","sticky?broadcast?send?ok!");}}
需要知道的是粘性廣播是普通廣播的一種,因此也可以使用普通廣播接收器來接收,當(dāng)然粘性廣播還有另一種常用的接收方式。
1.使用普通廣播接收器,注意,必須在Manifiest或者發(fā)送之前接收(這和定義有點違背,因為這種方式不是正確的接收方式)
public?class?StickyBroadcastReceiver?extends?BroadcastReceiver?{????public?static?final?String?Action?=?"com.sample.test.sticky.broadcast.receiver";????public?static?final?String?PERMISSION?=?"com.sample.test.permission.sticky.receiver";????@Override????public?void?onReceive(Context?context,?Intent?intent)????{????????int?checkCallingOrSelfPermission?=?context.checkCallingOrSelfPermission(PERMISSION);????????if(PackageManager.PERMISSION_GRANTED?==?checkCallingOrSelfPermission)?//權(quán)限判斷????????{????????????Toast.makeText(context,?"授權(quán)成功",?Toast.LENGTH_SHORT).show();????????}else{????????????Toast.makeText(context,?"授權(quán)失敗",?Toast.LENGTH_SHORT).show();????????????throw?new?RuntimeException("permission?denied");????????}????????if(intent!=null&&Action.equals(intent.getAction()))????????{????????????Toast.makeText(context,?intent.getStringExtra("info"),?Toast.LENGTH_SHORT).show();????????}????}}
Mainifest.xml
<!--自定義權(quán)限--><permission?android:name="com.sample.test.permission.sticky.receiver"?android:protectionLevel="normal"?/><!--使用自定義權(quán)限--><uses-permission?android:name="com.sample.test.permission.sticky.receiver"/><!--使用粘性廣播發(fā)送權(quán)限--><uses-permission?android:name="android.permission.BROADCAST_STICKY"?/><!--省略一部分--><receiver?android:name="test.view.weitop.home.StickyBroadcastReceiver"????android:permission="com.sample.test.permission.sticky.receiver"?>????<!--android:permission?其他應(yīng)用使用時,需要檢測的權(quán)限-->????<intent-filter?>?????????<action?android:name="com.sample.test.sticky.broadcast.receiver"/>????</intent-filter></receiver>
2.使用正確的方式接收(推薦)
正確的接收方式不應(yīng)該使用BroadcastReceiver就可以接收到
只需要將SenderActivity的onResume稍作修改即可
@Overrideprotected?void?onResume()?{????super.onResume();//3秒后發(fā)送?getWindow().getDecorView().postDelayed(new?Runnable()?{????@Override????????public?void?run()?????????{?????????sendStickyBroadcast();??????}????},?3*1000);//15秒后就收getWindow().getDecorView().postDelayed(new?Runnable()?{????@Override????public?void?run()????{????????IntentFilter?intentFilter?=?new?IntentFilter(StickyBroadcastReceiver.Action);????????Intent?data?=?registerReceiver(null,?intentFilter);????????if(data!=null&&StickyBroadcastReceiver.Action.equals(data.getAction()))????????{????????????Toast.makeText(this,?data.getStringExtra("info"),?Toast.LENGTH_SHORT).show();????????}????}????},?15*1000);}
在這樣一個相差10秒左右的時間段 【先發(fā)送,后接收】,說明了粘性廣播的已經(jīng)將信息完全保存起來了,只要我們?nèi)ナ褂萌缦路绞?,即可獲取到,而且無限次獲取。
?Intent?data?=?registerReceiver(null,?intentFilter);?//注意,不需要接收器,否則可能無法接收到
附注:這種廣播也可以被移除 ,我們可以接收到 廣播后調(diào)用 removeStickyBroadcast(intent);
本地廣播
? ? 前面所發(fā)送和接收的所用廣播都屬于系統(tǒng)的全局廣播,我們發(fā)出的這些廣播可以給系統(tǒng)中任何應(yīng)用程序接收到,當(dāng)然我們也可以接受其他應(yīng)用程序的廣播。但是,只用本地廣播機制發(fā)出的廣播只能在本應(yīng)用程序中接收到。這樣的話安全性得到了提升。本地廣播機制主要是用LocalBroadcastManager來管理,它提供了發(fā)送廣播的方法sendBroadcast()和sendBroadcastSync()(這也方法好像用的不多,我暫時沒有深究,等以后真正用到可以查一下),注冊接收者的方法registerReceiver(localReceiver, localIntentTilter),需要兩個參數(shù),取消注冊的方法localBroadcastManager.unregisterReceiver(localReceiver);
下面看一下本地廣播機制的工作流程:
1.發(fā)送本地廣播
?//發(fā)送本地廣播localBroadcastManager?=?LocalBroadcastManager.getInstance(this);?//獲取本地廣播管理實例localCastBtn?=?(Button)?findViewById(R.id.local_btn);localCastBtn.setOnClickListener(new?View.OnClickListener()?{????@Override????public?void?onClick(View?v)?{????????//發(fā)送本地廣播????????Intent?intent?=?new?Intent("com.zwf.broadcastdemo.LOCAL_BROADCAST");????????localBroadcastManager.sendBroadcast(intent);????}});
2.注冊廣播接收者
//注冊本地廣播監(jiān)聽localIntentTilter?=?new?IntentFilter("com.zwf.broadcastdemo.LOCAL_BROADCAST");localReceiver?=?new?LocalReceiver();localBroadcastManager.registerReceiver(localReceiver,?localIntentTilter);
3.取消注冊
//取消本地廣播監(jiān)聽localBroadcastManager.unregisterReceiver(localReceiver);
使用本地廣播的優(yōu)勢?
1. 可以明確地知道正在發(fā)送的廣播不會離開本程序,因此不需要擔(dān)心機密數(shù)據(jù)泄漏的問題。?
2. 其他程序無法將廣播發(fā)送到本程序的內(nèi)部,因此不需要擔(dān)心會有安全漏洞的隱患。?
3. 發(fā)送本地廣播比起發(fā)送系統(tǒng)全局廣播將會更加高效。
?