概念
作為Android的四大主件之一,這種組件本質(zhì)上是一種全局的監(jiān)聽(tīng)器,用于監(jiān)聽(tīng)系統(tǒng)全局的廣播消息,以及在應(yīng)用程序之間進(jìn)行信息的傳輸。
例如:
當(dāng)電池電量低、系統(tǒng)的時(shí)間變化、系統(tǒng)收到短信等等,這些狀態(tài)發(fā)生時(shí)系統(tǒng)會(huì)對(duì)外發(fā)送標(biāo)準(zhǔn)廣播,我們便可以通過(guò)繼承BroadcastReceiver來(lái)創(chuàng)建自己的廣播接收器監(jiān)聽(tīng)這些標(biāo)準(zhǔn)廣播,但廣播到達(dá)時(shí)執(zhí)行自己的操作。
使用場(chǎng)景
Android廣播分為兩個(gè)方面:廣播發(fā)送者和廣播接收者,通常情況下,BroadcastReceiver指的就是廣播接收者(廣播接收器)。廣播作為Android組件間的通信方式,可以使用的場(chǎng)景如下:
1.同一app內(nèi)部的同一組件內(nèi)的消息通信(單個(gè)或多個(gè)線程之間);----無(wú)意義 可以采用Handler 沒(méi)必要
2.同一app內(nèi)部的不同組件之間的消息通信(單個(gè)進(jìn)程);————應(yīng)用場(chǎng)景較少。
3.同一app具有多個(gè)進(jìn)程的不同組件之間的消息通信;
4.不同app之間的組件之間消息通信;
5.Android系統(tǒng)在特定情況下與App之間的消息通信。
其中3、4、5 需要對(duì)不同進(jìn)程間的消息通信,此時(shí)根據(jù)實(shí)際業(yè)務(wù)使用廣播機(jī)制會(huì)顯得非常適宜。
Android中的廣播使用了觀察者模式,基于消息的發(fā)布/訂閱事件模型。因此,從實(shí)現(xiàn)的角度來(lái)看,Android中的廣播將廣播的發(fā)送者和接受者極大程度上解耦,使得系統(tǒng)能夠方便集成,更易擴(kuò)展。
一、廣播創(chuàng)建
廣播創(chuàng)建不要太簡(jiǎn)單,作為四大主件之一,都有一共同的特點(diǎn)繼承
此處我們寫(xiě)一個(gè)類(lèi)繼承BroadcastReceiver,并重寫(xiě)其抽象方法onReceive()
//自定義廣播
public class MyBroadCastReceiver extends BroadcastReceiver {
//注冊(cè)的廣播行為,此處行為可以是任意字符串,只需要匹配我們廣播注冊(cè)時(shí)的行為就可以,行為可以是多個(gè)。
//這里我寫(xiě)的是系統(tǒng)短信到達(dá)的行為
public static final String INENT_ACTION="android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
//通過(guò)代碼 匹配 廣播發(fā)送時(shí)的行為,當(dāng)行為匹配,執(zhí)行我們的操作
if(intent.getAction().equals(INENT_ACTION)){
Log.e(TAG,"匹配到我們的意圖");
}
}
}
二、廣播注冊(cè)
想要監(jiān)聽(tīng)廣播,必然需要注冊(cè)屬于我們的廣播接收器
這里我們有兩種注冊(cè)方式:
1種: 代碼中動(dòng)態(tài)注冊(cè)
2種: 在Manifest.xml中靜態(tài)注冊(cè)
注冊(cè)方式不同,當(dāng)然是有所區(qū)別的
非常駐型:
當(dāng)你使用第一種方式注冊(cè)的時(shí)候,該廣播接收器不是常駐類(lèi)型,也就是說(shuō)廣播的接收器會(huì)跟隨主件,程序的生命周期銷(xiāo)毀而銷(xiāo)毀,一般我們會(huì)自己手動(dòng)進(jìn)行解除注冊(cè),避免內(nèi)存的泄漏。
常駐型:
當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來(lái),程序也會(huì)被系統(tǒng)調(diào)用自動(dòng)運(yùn)行(程序退出,進(jìn)程未被殺死的情況下)。
通過(guò)幾次測(cè)試,現(xiàn)在的一鍵殺死,殺死應(yīng)用程序進(jìn)程后無(wú)法被掉起,接著由于系統(tǒng)權(quán)限越來(lái)越??B,現(xiàn)在當(dāng)監(jiān)聽(tīng)到系統(tǒng)開(kāi)機(jī)廣播時(shí)也無(wú)法調(diào)起我們的App廣播。除非你的應(yīng)用程序被注冊(cè)到系統(tǒng)應(yīng)用程序內(nèi)。
注冊(cè)方式一:清單注冊(cè)
<receiver android:name=".MyBroadCastReceiver">
<!-- android:priority屬性是設(shè)置此接收者的優(yōu)先級(jí)(從-1000到1000)
用于有序廣播,值越大 , 優(yōu)先級(jí)越高
同級(jí)別接收是先后隨機(jī)的;級(jí)別低的收到廣播。
-->
<intent-filter android:priority="1000">
<!--注冊(cè)的廣播行為,此處是監(jiān)聽(tīng)的系統(tǒng)短信到達(dá)-->
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
注冊(cè)方式二:代碼注冊(cè)
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
MyBroadCastReceiver mRecevier;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 代碼注冊(cè)廣播,隨程序的生命周期
*/
mRecevier = new MyBroadCastReceiver();
IntentFilter filter = new IntentFilter();
//用于攔截短信的action
filter.setPriority(1000);
//行為定義,可以是多個(gè)
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
//代碼注冊(cè)廣播
registerReceiver(mRecevier,filter);
}
@Override
protected void onDestroy() {
//解除廣播注冊(cè)
unregisterReceiver(mRecevier);
super.onDestroy();
}
}
三、廣播發(fā)送
廣播的發(fā)送,分為兩種類(lèi)型:無(wú)序,有序
//無(wú)序:直接傳遞一個(gè)意圖
sendBroadcast(Intent intent)
//無(wú)序:傳遞一個(gè)意圖和權(quán)限
sendBroadcast(Intent intent, String receiverPermission)
//有序:傳遞一個(gè)意圖和權(quán)限
sendOrderedBroadcast(Intent intent,String receiverPermission)
//有序:傳遞一個(gè)意圖和權(quán)限附加額外信息
//resultReceiver:傳入一個(gè)receiver作為此次有序廣播最后一個(gè)執(zhí)行的廣播
//scheduler:指定要運(yùn)行的廣播接收器的線程。
//initialCode:初始碼 通過(guò)getResultCode()可以獲取
//initialData:初始數(shù)據(jù) 通過(guò)getResultData()可以獲取
//initialExtras:初始附加功能 通過(guò)getResultExtras(true)可以獲取
sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras)
代碼演示:
無(wú)序,無(wú)權(quán)限
Intent intent = new Intent();
intent.setAction(MyBroadCastReceiver.LOAD_MSG);
//發(fā)送無(wú)序廣播
sendBroadcast(intent);
無(wú)序,有權(quán)限
Intent intent = new Intent();
intent.setAction(MyBroadCastReceiver.LOAD_MSG);
//發(fā)送無(wú)序帶權(quán)限廣播
sendBroadcast(intent,"android.permission.RECEIVE_SMS");
有序,不帶額外參數(shù)
Intent intent = new Intent();
intent.setAction(MyBroadCastReceiver.LOAD_TEL);
//有序廣播:根據(jù)廣播優(yōu)先級(jí)
//第二個(gè)參數(shù) 為 接收的廣播 的 App所具備的權(quán)限 如果為NULL 則不需要權(quán)限
//如果傳遞了權(quán)限,只有在AndroidManifest.xml中要添加對(duì)應(yīng)的權(quán)限的應(yīng)用才能獲取到這條廣播。
sendOrderedBroadcast(intent,null);
sendOrderedBroadcast(intent,"android.permission.RECEIVE_SMS");
有序,帶額外參數(shù)
Intent intent = new Intent();
intent.setAction(MyBroadCastReceiver.LOAD_TEL);
Bundle extras = new Bundle();
extras.putString("extras","其它數(shù)據(jù)");
//此處權(quán)限為null,線程為默認(rèn)主線程
sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG,"最后一個(gè)被調(diào)用的receiver");
int resultCode = getResultCode();
String resultData = getResultData();
Bundle resultExtras = getResultExtras(true);
}
}, null, 10086, "初始數(shù)據(jù)", extras);
關(guān)于有序廣播:
既然廣播是一個(gè)有序的,那么我們就可以在中間攔截低級(jí)別的廣播,并中止廣播的傳遞。利用isOrderedBroadcast()判斷是否是有序廣播
利用abortBroadcast()攔截廣播
public class MyBroadCastReceiver2 extends BroadcastReceiver {
public static final String INENT_ACTION="android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
//自定義廣播 通過(guò)代碼注冊(cè)
if(intent.getAction().equals(INENT_ACTION)){
//判斷是否是 有序廣播
boolean orderedBroadcast = isOrderedBroadcast();
if (orderedBroadcast){
//獲取resultData
String resultData = getResultData();
//獲取resultCode
int resultCode = getResultCode();
//獲取resultExtras
Bundle resultExtras = getResultExtras(true);
if(resultExtras!=null){
//修改數(shù)據(jù)
resultExtras.putString("extras","你掉了10塊錢(qián)");
}
//重新設(shè)置result值,那么下一個(gè)廣播接受到的值就是我們修改過(guò)后的值.
setResult(40,"我修改了",resultExtras);
//如果不需要傳遞,可以使用abortBroadcast進(jìn)行攔截
abortBroadcast();
}
}
}
}
四、權(quán)限問(wèn)題
第一種場(chǎng)景: 誰(shuí)有權(quán)收我的廣播?
首先我們可以聲明一個(gè)自定義權(quán)限
在Androidmanifest.xml中定義
<!--自定義權(quán)限:
normal:低風(fēng)險(xiǎn)權(quán)限,只要申請(qǐng)了就可以使用,安裝時(shí)不需要用戶(hù)確認(rèn)
dangerous:高風(fēng)險(xiǎn)權(quán)限,安裝時(shí)需要用戶(hù)的確認(rèn)才可使用;
signature:只有當(dāng)申請(qǐng)權(quán)限的應(yīng)用程序的數(shù)字簽名與聲明此權(quán)限的應(yīng)用程序的數(shù)字簽名相同時(shí) (如果是申請(qǐng)系統(tǒng)權(quán)限,則需要與系統(tǒng)簽名相同),才能將權(quán)限授給它;
signatureOrSystem:簽名相同,或者申請(qǐng)權(quán)限的應(yīng)用為系統(tǒng)應(yīng)用(在system image中)。
一般就normal了
-->
<permission
android:name = "com.android.permission.my_permission"
android:protectionLevel="normal" />
<!-- 注冊(cè)自定義的權(quán)限 -->
<uses-permission android:name="com.android.permission.my_permission" />
做完以上操作,發(fā)送廣播時(shí)如果帶有此權(quán)限,那么只有注冊(cè)了此權(quán)限的應(yīng)用才能接收這條廣播。sendBroadcast(intent,"com.android.permission.my_permission");
第二種場(chǎng)景: 誰(shuí)有權(quán)給我發(fā)廣播?
當(dāng)我們?cè)谇鍐挝募凶?cè)廣播時(shí)可以加入權(quán)限,此處廣播上添加了權(quán)限。
android:permission="com.android.permission.my_permission"
<receiver android:name=".MyReceiver"
android:permission="com.android.permission.my_permission">
<intent-filter>
<action android:name="com.android.MY_ACTION" />
</intent-filter>
</receiver>
這樣一來(lái),我們定義的照顧Receiver只能接收來(lái)自具有我們注冊(cè)的權(quán)限的應(yīng)用發(fā)出的廣播了。需要進(jìn)行以下注冊(cè)
<!-- 注冊(cè)自定義的權(quán)限 -->
<uses-permission android:name="com.android.permission.my_permission" />
五、本地廣播
我們看出通過(guò)權(quán)限的控制,我們可以讓自己的廣播只被我們自己的應(yīng)用接收,但是太多的權(quán)限設(shè)置和發(fā)布比較繁瑣。
所以Google給我們提供了一枚專(zhuān)注于應(yīng)用內(nèi)的廣播 LocalBroadcastReceiver(此處的App應(yīng)用以App應(yīng)用進(jìn)程為界)
相比于全局廣播,App應(yīng)用內(nèi)廣播優(yōu)勢(shì)體現(xiàn)在:
1.安全性更高;
2.更加高效。
為此,Android v4兼容包中給出了封裝好的LocalBroadcastManager類(lèi),用于統(tǒng)一處理App應(yīng)用內(nèi)的廣播問(wèn)題,
使用方式上與通常的全局廣播幾乎相同,只是注冊(cè)/取消注冊(cè)廣播接收器和發(fā)送廣播時(shí)將主調(diào)context變成了LocalBroadcastManager的單一實(shí)例。
Android本地廣播機(jī)制:使用這個(gè)機(jī)制發(fā)出的廣播只能夠在應(yīng)用程序的內(nèi)部進(jìn)行傳遞.
并且廣播接收器也只能接收來(lái)自本應(yīng)用程序發(fā)出的廣播,這樣所有的安全性問(wèn)題就都不存在了。
本地廣播是無(wú)法通過(guò)靜態(tài)注冊(cè)的方式來(lái)接收的。因?yàn)橐脮r(shí)程序肯定已經(jīng)啟動(dòng)了。
注冊(cè)與解除注冊(cè)
/**
* LocalBroadcastManager 應(yīng)用內(nèi)廣播 只針對(duì)自身應(yīng)用廣播
*/
public class LocalReceiverActivity extends AppCompatActivity {
private LocalBroadcastManager instance;
private LocalReceiver localReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
//使用此方式注冊(cè)的廣播 只有本應(yīng)用程序才可以廣播到
instance = LocalBroadcastManager.getInstance(this);
localReceiver = new LocalReceiver();
//意圖過(guò)濾器
IntentFilter filter = new IntentFilter();
//添加行為
filter.addAction(LocalReceiver.FLAG);
instance.registerReceiver(localReceiver,filter);
}
@Override
protected void onDestroy() {
instance.unregisterReceiver(localReceiver);
super.onDestroy();
}
}
LocalReceiver 與我們之前的廣播創(chuàng)建沒(méi)區(qū)別都繼承至BroadcastReceiver。
public class LocalReceiver extends BroadcastReceiver {
public static final String FLAG = "localReceiver.com";
private static final String TAG =FLAG;
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG,"應(yīng)用內(nèi)廣播");
}
}
至此廣播就介紹完了,廣播功能雖然挺強(qiáng)大,不過(guò)在實(shí)際中用得還是比較少,應(yīng)用內(nèi)的通信我個(gè)人更喜歡用EventBus。