1.引言
在《第四章 Android 四大應(yīng)用組件》中佩谣,簡單介紹了下四大組件的成員凡怎,屬性悉稠,生命周期等。這里主要是介紹下Android中的廣播機制艘包。在四大組件中的猛,Activity的使用頻率是最高的。其他三個組件的使用頻率相對叫低想虎,但是既然叫四大組件卦尊,那就說明了他們的不可或缺性。
??記得小時候上學(xué)的時候的大喇叭嗎舌厨?有時候聽力考試也會用到岂却,學(xué)校的廣播室通過發(fā)送廣播到各個教室的小喇叭上,每次一開裙椭,那簡直是要命啊 躏哩。類似于喇叭的工作機制,在現(xiàn)在的計算機領(lǐng)域會有很廣泛的引用揉燃。為了便于系統(tǒng)級別的消息通知扫尺,Android也引入了廣播消息機制。當(dāng)然相應(yīng)的比較那個大喇叭例子炊汤,Android的廣播機制更加靈活正驻。
2.簡介
為什么說Android的廣播機制靈活呢?我們知道在我們的手機上有很多個應(yīng)用弊攘,有時候會接收到不同的應(yīng)用的消息,但是姑曙,既然是大喇叭襟交,發(fā)過來的消息,不是都可以接受么伤靠?這里簡單講一下它的靈活之處捣域,Android中的每個應(yīng)用程序都可以對自己感興趣的廣播注冊,這樣該程序就只能接收到自己關(guān)心的廣播內(nèi)容醋界。這些廣播的內(nèi)容可以是來自于系統(tǒng)竟宋,也可以是來自其他應(yīng)用程序。
??我們所使用的手機形纺,既可以說是一個廣播接收器丘侠,也可以說是一個廣播發(fā)送器。Android 的廣播分為兩個方面:廣播發(fā)送者和廣播接收者逐样。BroadCast Receiver指的是廣播接受者(廣播接收器)蜗字。在一些系統(tǒng)的廣播的使用場景:電量低彈窗,開機脂新,鎖屏等挪捕。常見的廣播使用場景有下面幾種:
1.同一app內(nèi)部的同一組件內(nèi)的消息通信(單個或多個線程之間)
2.同一app內(nèi)部的不同組件之間的消息通信(單個進程)智厌;
3.同一app具有多個進程的不同組件之間的消息通信套鹅;
4.不同app之間的組件之間消息通信仓犬;
5.Android系統(tǒng)在特定情況下與App之間的消息通信偶摔。
之所以叫做廣播卒密,就是因為它只管"說"至于"聽不聽"旨椒,那就不管了腌乡。另外廣播的另一個特點是盛龄,它可以被不止一個程序接收斩启,也可以不被任何程序接收序调。廣播機制最大的特點就是發(fā)送方并不關(guān)心接收方是否接到數(shù)據(jù),也不關(guān)心接收方是如何處理數(shù)據(jù)的兔簇。
Android中廣播的是操作系統(tǒng)中產(chǎn)生的各種各樣的事件发绢。例如,收到一條短信就會產(chǎn)生一個收到短信息的事件垄琐。而Android操作系統(tǒng)一旦內(nèi)部產(chǎn)生了這些事件边酒,就會向所有的廣播接收器對象來廣播這些事件。
3.BroadCast Receiver廣播接收器
3.1 廣播的類型
1.標準廣播
標準關(guān)閉時一種完全一步執(zhí)行的廣播此虑,假設(shè)有很多個廣播接收器甚纲,當(dāng)廣播發(fā)出去的時候,所有的接收器朦前,同時接收到廣播消息介杆。接收器之間沒有任何先后順序鹃操。這種廣播的頻率比較高,這也意味著它無法被截斷春哨。標準廣播的工作流程如下圖所示:
2.有序廣播
這是一種同步執(zhí)行的廣播荆隘,廣播接收器有先后之分,同一時刻只有一個接收器能夠接收廣播赴背,而且椰拒,當(dāng)優(yōu)先級靠前的接收器沒有接收到廣播的時候,優(yōu)先級靠后的接收器就無法接收到廣播了凰荚。而且優(yōu)先級高的廣播還能截斷廣播燃观。有序廣播的工作流程如下圖所示:
3.2 廣播接收器的注冊
1.靜態(tài)注冊
??靜態(tài)注冊方式是在AndroidManifest.xml的application里面定義receiver并設(shè)置要接收的action。如果在清單配置文件中配置了廣播接收器便瑟,那么程序在安裝后會自動注冊廣播接收器缆毁。
靜態(tài)注冊方式的特點:不管該應(yīng)用程序是否處于活動狀態(tài),都會進行監(jiān)聽到涂。程序即便未啟用脊框,也可以接收廣播。
實現(xiàn)靜態(tài)注冊的步驟:
1.創(chuàng)建一個類BootBroadCastReceiver繼承BroadcastReceiver類践啄,通過Android Studio創(chuàng)建浇雹,可以通過File->New->Other->Broadcast Receiver 這樣的快捷方式創(chuàng)建廣播接收器,AndroidManifest.xml文件會自動注冊屿讽。有一個<receiver> 標簽對昭灵。在onReceiver()中的方法很簡單,Toast一個就好了
public class BootBroadCastReceiver extends BroadcastReceiver {
private static final String TAG = "BootBroadCastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "啟動Broadcast", Toast.LENGTH_SHORT).show();
}
}
2.因為我們是需要接收廣播信息伐谈,所以需要權(quán)限RECEIVE_BOOT_COMPLETED虎锚。由于Android在啟動的時候會發(fā)出一條值為android.intent.action.BOOT_COMPLETED的廣播,所以 在<receiver>標簽對里面添加相應(yīng)的activity.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo.broadcastreceiverdemo">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".BootBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>
3.重啟模擬器,程序就可以接到開機廣播了效斑。
2.動態(tài)注冊
動態(tài)注冊也叫做代碼注冊非春,不需要在AndroidManifest.xml中注冊。而是在activity里面調(diào)用上下文對象的registerReceiver() 方法來注冊缓屠。和靜態(tài)的內(nèi)容差不多奇昙。一個形參是receiver對象,另一個是IntentFilter對象敌完。而IntentFilter構(gòu)造方法的參數(shù)是要接收的action储耐。
動態(tài)注冊方式特點:在代碼中進行注冊后,當(dāng)應(yīng)用程序關(guān)閉后滨溉,就不再進行監(jiān)聽什湘。
實現(xiàn)動態(tài)注冊的步驟,監(jiān)聽網(wǎng)絡(luò)狀態(tài):
1.創(chuàng)建一個類MyReceiver繼承BroadCast Receiver類长赞,重寫父類的onReceiver()方法,這樣當(dāng)網(wǎng)絡(luò)發(fā)生變化的時候,onReceiver的方法就會得到執(zhí)行闽撤。
class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "網(wǎng)絡(luò)連接狀態(tài)發(fā)生改變", Toast.LENGTH_SHORT).show();
}
}
2.在onCreate()方法中創(chuàng)建一個MyReceiver實例得哆,并創(chuàng)建一個IntentFilter的過濾器,指定action哟旗,因為當(dāng)網(wǎng)絡(luò)狀態(tài)發(fā)生改變的時候贩据,系統(tǒng)會發(fā)送android . net . conn . CONNECTIVITY _CHANGE這樣的廣播。如果想調(diào)用別的關(guān)閉可以使用相應(yīng)的action闸餐。最后在調(diào)用registerReceiver()方法饱亮,將myReceiver和intentFilter的實例傳進去,這樣就完成了注冊舍沙。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyReceiver myReceiver = new MyReceiver();
//創(chuàng)建過濾器近上,并制定action,使之用于接收同action的廣播
IntentFilter intentFilter=new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
//注冊廣播接收器
registerReceiver(myReceiver,intentFilter);
}
//創(chuàng)建過濾器的第二種寫法
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
3.最后在onDestory()的方法中調(diào)用 unregisterReceiver(),銷毀廣播,釋放內(nèi)存
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
4.因為Android 系統(tǒng)為了保護用戶設(shè)備的隱私场勤,所以對于一些相對于用戶來說的敏感權(quán)限需要在配置AndroidManifest.xml文件中添加權(quán)限聲明戈锻。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
3.3發(fā)送自定義廣播
??因為我們知道我們所適應(yīng)的實際既是廣播接收器,也是廣播發(fā)送器和媳。如果只能接格遭,不能發(fā),那就和收音機沒啥區(qū)別了留瞳。前面已經(jīng)介紹了拒迅,廣播主要分為兩種類型:標準廣播和有序廣播。這里介紹一下自定義的廣播她倘,相信你看完之后會很興奮的璧微,你可以定制你的Style了。
3.3.1 發(fā)送標準廣播
在思考發(fā)送廣播之前硬梁,我們需要有一個接收器前硫,這樣發(fā)出去的東西,才能知道它到底有沒有被接收到荧止。否則只管發(fā)了屹电,發(fā)了有沒有結(jié)果不知道,這就做了無用功了跃巡。注冊的方式危号,就選擇靜態(tài)注冊,因為我們是自定義的廣播素邪,所以外莲,系統(tǒng)啟動的時候,不會直接發(fā)送廣播兔朦。
具體步驟:
1.創(chuàng)建一個類MyBroadCastReceiver繼承 BroadcastReceiver偷线。這里就提示性的彈窗磨确。
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "快給我發(fā)過來啊,廣播", Toast.LENGTH_SHORT).show();
}
}
2.在Androidmanifest.xml文件中淋昭,修改receiver標簽俐填,添加需要發(fā)送的廣播。
<receiver
android:name=".myStyle.MyBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.demo.broadcastreceiverdemo.MY_BROADCAST" />
</intent-filter>
</receiver>
這里需要說明一下翔忽,我們在action標簽里面添加了一個com.demo.broadcastreceiverdemo. MY_BROADCAST的廣播英融。因此待會發(fā)送廣播的時候,我們就需要發(fā)送這樣的廣播歇式。然后在receiver里面接收同樣的廣播標簽驶悟。特別說明一下,格式是全包名+自定義材失。要不然會報錯痕鳍。你可以試試。
3.修改activity_main2.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<Button
android:id="@+id/button"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="發(fā)送廣播"/>
</LinearLayout>
4.發(fā)送廣播
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_BROADCAST");
sendBroadcast(intent);
}
});
}
}
我們把按鈕的點擊事件加入發(fā)送自定義廣播龙巨。首先構(gòu)建Intent對象笼呆,然后把要發(fā)送的廣播傳入,然后調(diào)用Context的sendBroadCast()的方法旨别,這樣所有監(jiān)聽com.demo.broadcastreceiverdemo. MY_BROADCAST的接收器就會接收到這條廣播信息诗赌。運行效果如圖所示:
這是一個簡單的小例子,我們通過在MainActivity中通過觸發(fā)點擊事件秸弛,來發(fā)送廣播铭若,并且自定義接收器,接收廣播递览。
3.3.2 發(fā)送有序廣播
在開篇的廣播介紹中叼屠,我們知道有序廣播的原理,應(yīng)用場景绞铃。我們知道廣播是可以跨進程進行通信的镜雨。因此在不同進程之間進行通信是可以實現(xiàn)的。為了測試有序廣播我們再新建一個project儿捧,然后在從第一個project開始發(fā)送廣播冷离,第二個project實現(xiàn)接受。
實現(xiàn)步驟:
1.新建一個project ,然后新建一個類SecondBroadCastReceiver繼承BroadCastReceiver.
public class SecondBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "第二個進程接收廣播", Toast.LENGTH_SHORT).show();
}
2.給第二個AndroidMainifest.xml文件中纯命,修改receiver方法,設(shè)置action接受的廣播與第一個project的一樣com.demo.broadcastreceiverdemo.MY_BROADCAST痹栖。
<receiver
android:name=".SecondBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="com.demo.broadcastreceiverdemo.MY_BROADCAST"/>
</intent-filter>
</receiver>
我們可以看到第一個和第一個project都可以接收同樣的廣播com.demo.broadcastreceiverdemo. MY_BROADCAST亿汞。然后回到第一個project界面,運行結(jié)果如下圖所示:
我們可以看到揪阿,我們接受了兩次信息疗我,分別為MyBroadCastReceiver和SecondBroadCastReceiver咆畏,所以我們的應(yīng)用程序之間可以被通信。
不過到目前為止吴裤,我們的程序里發(fā)出的都是標準廣播旧找,哪有序廣播呢?回到第一個project項目麦牺。
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_BROADCAST");
// sendBroadcast(intent);
sendOrderedBroadcast(intent,null);
}
});
}
}
修改intent的發(fā)送方法钮蛛, sendOrderedBroadcast()。 這個方法接收兩個參數(shù)剖膳,第一個參數(shù)為intent,,第二個參數(shù)是一個與權(quán)限相關(guān)的字符串魏颓。這里傳個空值就可以了。然后重新運行第一個project吱晒,發(fā)現(xiàn)兩個應(yīng)用程序還是可以接收廣播甸饱。
看上去好像沒有什么區(qū)別,但是我們知道有序廣播仑濒,是串行廣播叹话,是有優(yōu)先級的,廣播可以被攔截的墩瞳。
1.設(shè)置優(yōu)先級:
<receiver
android:name=".myStyle.MyBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.demo.broadcastreceiverdemo.MY_BROADCAST" />
</intent-filter>
</receiver>
在 intent-filter 添加 android:priority="100"驼壶,數(shù)字可以自己設(shè)置。數(shù)字越大矗烛,優(yōu)先級越高辅柴。當(dāng)你將第二個project的 action 的值設(shè)置比第一個大,彈窗會第二個較第一個早瞭吃。
2.攔截廣播
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "快給我發(fā)過來啊碌嘀,廣播", Toast.LENGTH_SHORT).show();
abortBroadcast();//攔截廣播
}
}
在重寫的onReceive()的方法里面加上 abortBroadcast()方法,這樣就完成了攔截廣播的操作歪架。
4.使用本地廣播
我們之前講的廣播無論是發(fā)送還是接收全部都屬于系統(tǒng)全局廣播股冗,發(fā)出的廣播可以被任何應(yīng)用程序接收,而且也可以接收任何應(yīng)用程序的廣播和蚪,所以這就會引發(fā)安全問題止状。通過廣播發(fā)送的信息被其他程序截獲了,或者別的程序不同的發(fā)送垃圾廣播給你攒霹,那還得了怯疤。
所以為了能夠解決安全性的問題,Android引入了本地廣播消息機制催束,這樣發(fā)送的廣播消息都只能在本應(yīng)用程序里面?zhèn)鬟f集峦,廣播接收器只能接收來自本應(yīng)用的消息,這樣就不存在安全性問題了。代碼如下:
1.在MainActivity中塔淤,定義一個內(nèi)部類集成BroadcCastReceiver
class LocalBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受本地的廣播消息", Toast.LENGTH_SHORT).show();
}
}
2.設(shè)置發(fā)送的廣播和接收的廣播摘昌。我們這里因為要用到本地廣播,所以需要引入LocalBroadcastManager本地廣播管理類高蜂。通過getInstance()方法獲取實例聪黎,然后通過本地廣播調(diào)用sendBroadcast()去發(fā)送廣播,然后設(shè)置過濾器备恤,過濾接收的廣播action稿饰,最后實例化本地廣播接收器,去注冊本地廣播烘跺。
private LocalBroadcastManager localBroadcastManager;
private IntentFilter intentFilter;
private LocalBroadCastReceiver localBroadCastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
//獲取實例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button= (Button) findViewById(R.id.button3);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadCastReceiver = new LocalBroadCastReceiver();
localBroadcastManager.registerReceiver(localBroadCastReceiver,intentFilter);//注冊本地廣播監(jiān)聽器
}
3.還記得代碼注冊廣播湘纵,要銷毀
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localBroadCastReceiver);
}
完整代碼如下:
public class Main3Activity extends AppCompatActivity {
private LocalBroadcastManager localBroadcastManager;
private IntentFilter intentFilter;
private LocalBroadCastReceiver localBroadCastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
//獲取實例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button= (Button) findViewById(R.id.button3);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadCastReceiver = new LocalBroadCastReceiver();
localBroadcastManager.registerReceiver(localBroadCastReceiver,intentFilter);//注冊本地廣播監(jiān)聽器
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localBroadCastReceiver);
}
class LocalBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受本地的廣播消息", Toast.LENGTH_SHORT).show();
}
}
}
實現(xiàn)效果如下所示:
最后奉上github地址:https://github.com/wangxin3119/BroadCastDemo