1.引言
在《第四章 Android 四大應用組件》中,簡單介紹了下四大組件的成員,屬性,生命周期等。這里主要是介紹下Android中的廣播機制。在四大組件中,Activity的使用頻率是最高的。其他三個組件的使用頻率相對叫低,但是既然叫四大組件,那就說明了他們的不可或缺性。
??記得小時候上學的時候的大喇叭嗎?有時候聽力考試也會用到,學校的廣播室通過發送廣播到各個教室的小喇叭上,每次一開,那簡直是要命啊 。類似于喇叭的工作機制,在現在的計算機領域會有很廣泛的引用。為了便于系統級別的消息通知,Android也引入了廣播消息機制。當然相應的比較那個大喇叭例子,Android的廣播機制更加靈活。
2.簡介
為什么說Android的廣播機制靈活呢?我們知道在我們的手機上有很多個應用,有時候會接收到不同的應用的消息,但是,既然是大喇叭,發過來的消息,不是都可以接受么?這里簡單講一下它的靈活之處,Android中的每個應用程序都可以對自己感興趣的廣播注冊,這樣該程序就只能接收到自己關心的廣播內容。這些廣播的內容可以是來自于系統,也可以是來自其他應用程序。
??我們所使用的手機,既可以說是一個廣播接收器,也可以說是一個廣播發送器。Android 的廣播分為兩個方面:廣播發送者和廣播接收者。BroadCast Receiver指的是廣播接受者(廣播接收器)。在一些系統的廣播的使用場景:電量低彈窗,開機,鎖屏等。常見的廣播使用場景有下面幾種:
1.同一app內部的同一組件內的消息通信(單個或多個線程之間)
2.同一app內部的不同組件之間的消息通信(單個進程);
3.同一app具有多個進程的不同組件之間的消息通信;
4.不同app之間的組件之間消息通信;
5.Android系統在特定情況下與App之間的消息通信。
之所以叫做廣播,就是因為它只管"說"至于"聽不聽",那就不管了。另外廣播的另一個特點是,它可以被不止一個程序接收,也可以不被任何程序接收。廣播機制最大的特點就是發送方并不關心接收方是否接到數據,也不關心接收方是如何處理數據的。
Android中廣播的是操作系統中產生的各種各樣的事件。例如,收到一條短信就會產生一個收到短信息的事件。而Android操作系統一旦內部產生了這些事件,就會向所有的廣播接收器對象來廣播這些事件。
3.BroadCast Receiver廣播接收器
3.1 廣播的類型
1.標準廣播
標準關閉時一種完全一步執行的廣播,假設有很多個廣播接收器,當廣播發出去的時候,所有的接收器,同時接收到廣播消息。接收器之間沒有任何先后順序。這種廣播的頻率比較高,這也意味著它無法被截斷。標準廣播的工作流程如下圖所示:
2.有序廣播
這是一種同步執行的廣播,廣播接收器有先后之分,同一時刻只有一個接收器能夠接收廣播,而且,當優先級靠前的接收器沒有接收到廣播的時候,優先級靠后的接收器就無法接收到廣播了。而且優先級高的廣播還能截斷廣播。有序廣播的工作流程如下圖所示:
3.2 廣播接收器的注冊
1.靜態注冊
??靜態注冊方式是在AndroidManifest.xml的application里面定義receiver并設置要接收的action。如果在清單配置文件中配置了廣播接收器,那么程序在安裝后會自動注冊廣播接收器。
靜態注冊方式的特點:不管該應用程序是否處于活動狀態,都會進行監聽。程序即便未啟用,也可以接收廣播。
實現靜態注冊的步驟:
1.創建一個類BootBroadCastReceiver繼承BroadcastReceiver類,通過Android Studio創建,可以通過File->New->Other->Broadcast Receiver 這樣的快捷方式創建廣播接收器,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.因為我們是需要接收廣播信息,所以需要權限RECEIVE_BOOT_COMPLETED。由于Android在啟動的時候會發出一條值為android.intent.action.BOOT_COMPLETED的廣播,所以 在<receiver>標簽對里面添加相應的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.動態注冊
動態注冊也叫做代碼注冊,不需要在AndroidManifest.xml中注冊。而是在activity里面調用上下文對象的registerReceiver() 方法來注冊。和靜態的內容差不多。一個形參是receiver對象,另一個是IntentFilter對象。而IntentFilter構造方法的參數是要接收的action。
動態注冊方式特點:在代碼中進行注冊后,當應用程序關閉后,就不再進行監聽。
實現動態注冊的步驟,監聽網絡狀態:
1.創建一個類MyReceiver繼承BroadCast Receiver類,重寫父類的onReceiver()方法,這樣當網絡發生變化的時候,onReceiver的方法就會得到執行。
class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "網絡連接狀態發生改變", Toast.LENGTH_SHORT).show();
}
}
2.在onCreate()方法中創建一個MyReceiver實例,并創建一個IntentFilter的過濾器,指定action,因為當網絡狀態發生改變的時候,系統會發送android . net . conn . CONNECTIVITY _CHANGE這樣的廣播。如果想調用別的關閉可以使用相應的action。最后在調用registerReceiver()方法,將myReceiver和intentFilter的實例傳進去,這樣就完成了注冊。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyReceiver myReceiver = new MyReceiver();
//創建過濾器,并制定action,使之用于接收同action的廣播
IntentFilter intentFilter=new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
//注冊廣播接收器
registerReceiver(myReceiver,intentFilter);
}
//創建過濾器的第二種寫法
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
3.最后在onDestory()的方法中調用 unregisterReceiver(),銷毀廣播,釋放內存
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
4.因為Android 系統為了保護用戶設備的隱私,所以對于一些相對于用戶來說的敏感權限需要在配置AndroidManifest.xml文件中添加權限聲明。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
3.3發送自定義廣播
??因為我們知道我們所適應的實際既是廣播接收器,也是廣播發送器。如果只能接,不能發,那就和收音機沒啥區別了。前面已經介紹了,廣播主要分為兩種類型:標準廣播和有序廣播。這里介紹一下自定義的廣播,相信你看完之后會很興奮的,你可以定制你的Style了。
3.3.1 發送標準廣播
在思考發送廣播之前,我們需要有一個接收器,這樣發出去的東西,才能知道它到底有沒有被接收到。否則只管發了,發了有沒有結果不知道,這就做了無用功了。注冊的方式,就選擇靜態注冊,因為我們是自定義的廣播,所以,系統啟動的時候,不會直接發送廣播。
具體步驟:
1.創建一個類MyBroadCastReceiver繼承 BroadcastReceiver。這里就提示性的彈窗。
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "快給我發過來啊,廣播", Toast.LENGTH_SHORT).show();
}
}
2.在Androidmanifest.xml文件中,修改receiver標簽,添加需要發送的廣播。
<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的廣播。因此待會發送廣播的時候,我們就需要發送這樣的廣播。然后在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="發送廣播"/>
</LinearLayout>
4.發送廣播
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);
}
});
}
}
我們把按鈕的點擊事件加入發送自定義廣播。首先構建Intent對象,然后把要發送的廣播傳入,然后調用Context的sendBroadCast()的方法,這樣所有監聽com.demo.broadcastreceiverdemo. MY_BROADCAST的接收器就會接收到這條廣播信息。運行效果如圖所示:
這是一個簡單的小例子,我們通過在MainActivity中通過觸發點擊事件,來發送廣播,并且自定義接收器,接收廣播。
3.3.2 發送有序廣播
在開篇的廣播介紹中,我們知道有序廣播的原理,應用場景。我們知道廣播是可以跨進程進行通信的。因此在不同進程之間進行通信是可以實現的。為了測試有序廣播我們再新建一個project,然后在從第一個project開始發送廣播,第二個project實現接受。
實現步驟:
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方法,設置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界面,運行結果如下圖所示:
我們可以看到,我們接受了兩次信息,分別為MyBroadCastReceiver和SecondBroadCastReceiver,所以我們的應用程序之間可以被通信。
不過到目前為止,我們的程序里發出的都是標準廣播,哪有序廣播呢?回到第一個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的發送方法, sendOrderedBroadcast()。 這個方法接收兩個參數,第一個參數為intent,,第二個參數是一個與權限相關的字符串。這里傳個空值就可以了。然后重新運行第一個project,發現兩個應用程序還是可以接收廣播。
看上去好像沒有什么區別,但是我們知道有序廣播,是串行廣播,是有優先級的,廣播可以被攔截的。
1.設置優先級:
<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",數字可以自己設置。數字越大,優先級越高。當你將第二個project的 action 的值設置比第一個大,彈窗會第二個較第一個早。
2.攔截廣播
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "快給我發過來啊,廣播", Toast.LENGTH_SHORT).show();
abortBroadcast();//攔截廣播
}
}
在重寫的onReceive()的方法里面加上 abortBroadcast()方法,這樣就完成了攔截廣播的操作。
4.使用本地廣播
我們之前講的廣播無論是發送還是接收全部都屬于系統全局廣播,發出的廣播可以被任何應用程序接收,而且也可以接收任何應用程序的廣播,所以這就會引發安全問題。通過廣播發送的信息被其他程序截獲了,或者別的程序不同的發送垃圾廣播給你,那還得了。
所以為了能夠解決安全性的問題,Android引入了本地廣播消息機制,這樣發送的廣播消息都只能在本應用程序里面傳遞,廣播接收器只能接收來自本應用的消息,這樣就不存在安全性問題了。代碼如下:
1.在MainActivity中,定義一個內部類集成BroadcCastReceiver
class LocalBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受本地的廣播消息", Toast.LENGTH_SHORT).show();
}
}
2.設置發送的廣播和接收的廣播。我們這里因為要用到本地廣播,所以需要引入LocalBroadcastManager本地廣播管理類。通過getInstance()方法獲取實例,然后通過本地廣播調用sendBroadcast()去發送廣播,然后設置過濾器,過濾接收的廣播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);//注冊本地廣播監聽器
}
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);//注冊本地廣播監聽器
}
@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();
}
}
}
實現效果如下所示:
最后奉上github地址:https://github.com/wangxin3119/BroadCastDemo