Android筆記---四大元件之Broadcast Receive廣播接收器詳解
廣播(Broadcast)是一種廣泛運用的在應用程式之間傳輸資訊的機制,在 Android 裡面有各種各樣的廣播,比如電池的使用狀態,電話的接收和簡訊的接收都會產生一個廣播,應用程式也可以接受廣播並做出程式邏輯上的處理, 比如我們需要讓應用程式開機自動啟動,其實就是應用了廣播的只是,讓應用程式監聽接收系統開機廣播來啟動程式,我後面會以這個例子來具體看下廣播的使用,還有比如手機電量低於20%的情況下出現一個提醒的對話方塊也是利用廣播的原理。
關於廣播,我們需要掌握瞭解廣播的三要素:
1.廣播(Broadcast) - 用於傳送廣播;
2.廣播接收器(BroadcastReceiver) - 用於接收廣播;
3.意圖(Intent)-用於儲存廣播相關資訊的媒介。
先簡單介紹一下廣播中需要掌握的一些概念,後面給出在程式碼裡面的用法。
1.普通廣播
普通廣播是完全非同步的,可以在同一時刻(邏輯上)被所有廣播接收者接收到,訊息傳遞的效率比較高,但缺點是接收者不能將處理結果傳遞給下一個接收者,並且無法終止廣播Intent的傳播。
2.有序廣播
有序廣播是按照接收者宣告的優先級別(宣告在intent-filter元素的android:priority屬性中,數越大優先級別越高,取值範圍:-1000到1000。也可以呼叫IntentFilter物件的setPriority()進行設定),被接收者依次接收廣播。如:A的級別高於B, B的級別高於C,那麼,廣播先傳給A,再傳給B,最後傳給C。A得到廣播後,可以往廣播裡存入資料,當廣播傳給B時,nB可以從廣播中得到A存入的資料。
3系統廣播
系統廣播是Android內建的廣播,滿足一定條件後系統會自動傳送這個廣播,不需要我們再定義傳送,只需要用的時候接收就可以了,比如手機開機完成後會發出一條廣播,電池的電量發生變化會發出一 條廣播,時間或時區發生改變也會發出一條廣播,攝像頭按被按下會觸發廣播等等。
4自定義廣播
自定義廣播是使用者可以自己定義傳送一個廣播,看到很多書籍都是這麼講,可以我一直在思考為什麼需要自定義廣播,或者說自定義廣播在Android程式中一般是用在什麼地方,因為廣播是為了給不同的元件中通訊而使用的例如service 與 Activity 不同的程序中,或者不同的應用程式中就會使用廣播機制來通訊的。
5本地廣播
系統廣播和自定義廣播都屬於全域性廣播,即發出的廣播可以被其他任何 的任何應用程式接收到, 並且我們也可以接收來自於其他任何應用程式的廣播。這樣就很容易會引起安全性的問題, 比如說我們傳送的一些攜帶關鍵性資料的廣播有可能被其他的應用程式截獲,或者其他的程式不停地向我們的廣播接收器裡傳送各種垃圾廣播。
為了能夠簡單地解決廣播的安全性問題,Android 引入了一套本地廣播機制,使用這個機制發出的廣播只能夠在應用程式的內部進行傳遞,並且廣播接收器也只能接收來自本應用程式發出的廣播,這樣所有的安全性問題就都不存在了。
6動態註冊廣播
動態註冊就是在程式中使用Context.registerReceiver註冊。動態註冊的廣播接收器可以自由地控制註冊與登出,在靈活性方面有很大的優勢,但是它也存在著一個缺點,即必須要在程式啟動之後才能接收到廣播,因為註冊的邏輯是寫在 onCreate()方法中的。
7靜態註冊廣播
在AndroidManifest.xml中註冊廣播稱之為靜態註冊,靜態註冊是常駐型 ,也就是說當應用程式關閉後,如果有資訊廣播來,程式也會被系統呼叫自動執行,所以一些系統級別的廣播比如開機啟動廣播就必須要用到靜態註冊的方法,而有些系統廣播也是可以動態註冊的,比如電量變化廣播等可以在程式啟動後監聽即可。
以下我們以一些具體的例子來講解下廣播的用法,先看讓一個應用程式接收開機廣播的例子:
需要先對自己感興趣的廣播進行註冊(這裡當然是開機廣播),正如前面說的這種情況只能在AndroidManifest.xml使用靜態註冊的方式,所有靜態註冊的廣播接收器都是在receiver標籤進行註冊的,下面給出AndroidManifest.xml檔案的程式碼以及相關使用註釋:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidstudybroadcastreceive"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
//監聽系統開機廣播也是需要宣告許可權的,需要新增如下許可權
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//所有靜態註冊的廣播接收器都是在receiver標籤進行註冊的
//需要將將廣播接收器的類名註冊進來,我是新建了一個類MyBroadcastReceive
<receiver android:name=".MyBroadcastReceive">
<intent-filter>
//通過android:name來指定具體註冊哪一個廣播接收器
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
註冊後需要建立一個廣播接收器,其實只需要新建一個類, 讓它繼承自BroadcastReceiver, 並重寫父類的onReceive()方法就行了。這樣當有廣播到來時,onReceive()方法就會得到執行, 具體的邏輯就可以在這個方法中處理。下面給出我新建的類MyBroadcastReceive裡面的程式碼如下:
package com.example.androidstudybroadcastreceive;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//列印一個字串啟動完成
Toast.makeText(context, "啟動完成", Toast.LENGTH_LONG).show();
}
}
這裡啟動模擬器Genymotion完成後會看到有輸出啟動完成四個字,說明上面這個應用程式成功接收到了開機廣播並完成了onReceive()方法中的Toast方法。
上面的例子中我們用到的知識點是系統廣播以及靜態註冊廣播,我們再來看一自定義廣播通過靜態註冊的例子:
首先,和系統廣播一樣,先在AndroidManifest.xml檔案中新增一個自定義廣播:
<receiver android:name=".MyBroadcastReceive">
<intent-filter>
//通過android:name來指定具體註冊哪一個廣播接收器,這裡可以自己任意指定name
<action android:name="com.dxy.mybroadcast" />
</intent-filter>
</receiver>
然後在onCreate()方法中點選Button傳送廣播:
public class MainActivity extends Activity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button) findViewById(R.id.bt);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.dxy.mybroadcast");
//攜帶自定義的一些資料
intent.putExtra("name", "dxy");
sendBroadcast(intent);
}
});
}
}
最後接收這個廣播並完成邏輯程式碼的編寫:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//獲取廣播過來的資料
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一個程式收到了廣播,name為 "+name);
}
}
再來看動態註冊的方式傳送自定義廣播,並且瞭解有序廣播,普通廣播的概念。
先來看下有序廣播, 這裡以2個Application為例:
Application1中我們需要傳送廣播以及接收廣播:
註冊廣播:使用Context.registerReceiver註冊。
傳送廣播:通過Context.sendBroadcast來發送,由Intent來傳遞註冊時用到的Action。
接收廣播:當傳送的廣播被接收器監聽到後,會呼叫onReceive()方法,並將包含訊息的Intent物件傳回。
取消廣播:動態註冊的廣播接收器一定都要取消註冊才行。
package com.example.androidstudybroadcastreceive;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button;
//定義意圖過濾器
private IntentFilter filter;
//定義廣播接收器
BroadcastReceiveTest myBroadCast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button) findViewById(R.id.bt);
//例項化廣播接收器
myBroadCast = new BroadcastReceiveTest();
//例項化意圖過濾器
filter = new IntentFilter();
//為過濾器新增一個定義的廣播,當然這裡也可以填系統廣播
filter.addAction("com.dxy.myBroadCast");
//可以通過setPriority()設定動態廣播的優先順序,優先順序取值範圍是-1000到1000
//注意的是的靜態註冊的廣播要快於動態註冊的廣播,不管動態註冊的優先順序設定的多高,不管靜態註冊的優先順序有多低
filter.setPriority(1000);
//註冊廣播
registerReceiver(myBroadCast, filter);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//首先構建出了一 個 Intent 物件,並把要傳送的廣播的值傳入
Intent intent = new Intent("com.dxy.myBroadCast");
//攜帶自定義的一些資料
intent.putExtra("name", "dxy");
//呼叫sendBroadcast()方法傳送普通廣播
sendBroadcast(intent);
}
});
}
//在onDestroy()方法中呼叫unregisterReceiver()取消廣播註冊,當然也可以放在onStop()方法中取消,如果不取消註冊程式退出是會報錯的。
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myBroadCast);
}
}
//這裡直接用了一個內部類來接收廣播,當然也可以另外用一個獨立的外部內來接收
class BroadcastReceiveTest extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//獲取廣播過來的資料
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一個程式收到了廣播,name為 "+name);
}
}
Application2中我們要做的是接收Application1中傳送的廣播,程式碼比較簡單:
先註冊一個廣播:
<receiver android:name=".AnotherBroadcastReceive">
<intent-filter>
//通過android:name來指定具體註冊監聽Application1中的廣播com.dxy.myBroadCast
<action android:name="com.dxy.myBroadCast" />
</intent-filter>
</receiver>
然後新建一個類MyBroadcastReceive類繼承BroadcastReceiver重寫onReceive()方法:
package com.example.androidstudybroadcastreceuve2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class AnotherBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第二個程式收到了廣播,name為 "+name);
}
}
此時我們執行Application1並點選按鈕傳送一個廣播,Logcat的截圖如下:
我們可以看到Application1和Application2都接收到了這個廣播並且都做了相應的處理,但是Application2確要比Application1優先收到廣播,是因為靜態註冊的廣播要快於動態註冊的廣播,我們下面將Application1中註冊方式改為靜態註冊的方式並且設定優先順序為1000:
<receiver android:name=".MyBroadcastReceive">
<intent-filter android:priority="1000">
//通過android:name來指定具體註冊哪一個廣播接收器
<action android:name="com.dxy.myBroadCast" />
</intent-filter>
</receiver>
Application2中設定優先順序為999:
<receiver android:name=".AnotherBroadcastReceive">
<intent-filter android:priority="999">
//通過android:name來指定具體註冊哪一個廣播接收器
<action android:name="com.dxy.myBroadCast" />
</intent-filter>
</receiver>
再執行看下Logcat執行的情況Application1比Application2先收到廣播:
這裡再強調一點是的普通廣播發送後都是可以收到的,不能擷取後終止,比如我在Application1中onReceive()方法呼叫abortBroadcast()方法終止廣播Application2還是可以收到的。
現在我們來看下有序廣播,其實有序廣播只需要將Application1中的sendBroadcast()方法改成sendOrderedBroadcast()就可以了,但是需要注意的是sendOrderedBroadcast()方法需要傳入兩個引數:
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//首先構建出了一 個 Intent 物件,並把要傳送的廣播的值傳入
Intent intent = new Intent("com.dxy.myBroadCast");
//攜帶自定義的一些資料
intent.putExtra("name", "dxy");
//呼叫sendBroadcast()方法傳送普通廣播
//sendBroadcast(intent);
//呼叫sendOrderedBroadcast()方法傳送普通廣播
sendOrderedBroadcast(intent,null);
}
});
我們在Application1中的onReceive()方法中呼叫abortBroadcast()方法結束廣播:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一個程式收到了廣播,name為 "+name);
abortBroadcast();
}
}
這個時候執行程式點擊發送廣播就只有Application1能收到廣播了:
另外,因為我們的Application1現獲取廣播,我們可以往廣播裡存入資料傳給下一個廣播Application2
Application1廣播裡存入資料:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一個程式收到了廣播,name為 "+name);
Bundle bundle = new Bundle();
bundle.putString("next_receiver", "Application1廣播傳過來的");
setResultExtras(bundle);
}
}
Application2廣播裡接收資料:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Bundle bundle = getResultExtras(true);
String content = bundle.getString("next_receiver");
Log.d("mybroadcast","第二個程式收到了了第一個程式傳送過來的資料 "+content);
}
}
再來看下Logcat顯示的情況就瞭解了:
最後簡單看下本地廣播,本地廣播是提供LocalBroadcastManager來管理的,需要注意的是,本地廣播是無法通過靜態註冊的方式來接收的。因為靜態註冊主要就是為了讓程式在未啟動的情況下也能收到廣播, 而傳送本地廣播時,我們的程式肯定是已經啟動了,因此也完全不需要使用靜態註冊的功能。
這裡直接給出程式碼:
package com.example.androidstudybroadcastreceive;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button;
private IntentFilter filter;
BroadcastReceiveTest myBroadCast;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button) findViewById(R.id.bt);
//通過 LocalBroadcastManager 的 getInstance() 方法得到了它的一個例項
localBroadcastManager = LocalBroadcastManager.getInstance(this);
myBroadCast = new BroadcastReceiveTest();
filter = new IntentFilter();
//過濾條件的廣播
filter.addAction("com.dxy.myBroadCast");
//註冊廣播接收器
localBroadcastManager.registerReceiver(myBroadCast, filter);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.dxy.myBroadCast");
intent.putExtra("name", "dxy");
localBroadcastManager.sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(myBroadCast);
}
}
class BroadcastReceiveTest extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一個程式收到了廣播,name為 "+name);
}
}
執行結果只有Application1能收到廣播了: