BroadcastReceiver使用介紹

BroadcastReceiver 介紹

原文出處:http://www.ccbu.cc/index.php/android/broadcast_receiver_intro.html

BroadcastReceiver是Android開發4大組件之一。正如其名BroadcastReceiver作爲廣播接收者,用來監聽系統全局的廣播消息。廣播(Broadcast)在Android系統中是在各組件之間傳播數據的一種機制,這些組件可以位於不同的進程中。作爲一個全局的廣播監聽組件,BroadcastReceiver可以很容易的實現系統中不同組件、不同應用之間的通信。

BroadcastReceiver 是對發送出來的 Broadcast 進行過濾、接受和響應的組件。而廣播則被裝入一個 Intent 對象,然後通過調用系統提供的 Context.sendBroadcast()Context.sendOrderBroadcast() 將該 Intent 對象已廣播的形式發送出去。發出的Intent對象需要包括一個action信息,Category信息以及一下其他extra信息。廣播發出去之後,所有註冊了該action的 BroadcastReceiver 則會收到發出的廣播。BroadcastReceiver 通過檢查接收的廣播的action,Category等信息進行相應的處理。

BroadcastReceiver 接收廣播的處理十分簡單,在應用開發中,我們只用要重寫BroadcastReceiver的onReceive(Context context, Intent intent)方法就可以。

class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "Receiver Broadcast: " + intent.getAction());
    }
}

BroadcastReceiver 的註冊

  1. 靜態註冊
    靜態註冊即在 AndroidManifest.xml 清單文件中爲 BroadcastReceiver 進行註冊,使用 < receiver > 標籤聲明,並在標籤內用 < intent-filter > 標籤設置過濾器。這種形式的 BroadcastReceiver 的生命週期伴隨着整個應用,如果這種方式處理的是系統廣播,那麼不管應用是否在運行,該廣播接收器都能接收到該廣播(非系統app需要運行過一次,且有權限接收該廣播)。
<receiver android:name=".MyBroadcastReceiver">  
    <intent-filter>  
        <action android:name="android.intent.action.test"/>  
    </intent-filter>  
</receiver>
  1. 動態註冊
    動態註冊 BroadcastReceiver 是在代碼中定義並設置好一個 IntentFilter 對象,然後在需要註冊的地方調用 Context.registerReceiver() 方法,調用Context.unregisterReceiver() 方法取消註冊
MyBroadcastReceiver mReceiver = new MyBroadcastReceiver();          
IntentFilter filter = new IntentFilter();  
filter.addAction("android.intent.action.test"); 

registerReceiver(mReceiver, filter); 
unregisterReceiver(mReceiver);

廣播發送

發送廣播有兩種方式:

  • sendBroadcast 普通廣播
  • sendOrderedBroadcast 有序廣播
  1. 普通廣播
private static final String ACTION = "android.intent.action.test";
Intent intent = new Intent(ACTION);
sendBroadcast(intent);

發送普通廣播,對於多個註冊了該action的廣播,都能一塊接收到,並沒有接收的先後順序。也沒有辦法阻止另一個接收者接收這個廣播。

  1. 有序廣播
private static final String ACTION = "android.intent.action.test";
Intent intent = new Intent(ACTION);
sendOrderedBroadcast(intent, null);

發送有序廣播, 有多個廣播接收者,則按順序接收廣播,高優先級的先收到,然後是低優先級的。優先級一般在註冊的時候通過 priority 屬性來進行設置,優先級從-1000~1000,數越大,優先級越高。另外。優先級高的 Receiver 也能在處理完操作後向優先級低的 Receiver 傳送處理結果。此外,高優先級的 BroadcastReceiver 也能調用 abortBroadcast() 方法截斷廣播,這樣低優先級的廣播接收器就無法接收到廣播了。

<receiver android:name=".MyBroadcastReceiver1">  
    <intent-filter android:priority="100">  
        <action android:name="android.intent.action.test"/>  
    </intent-filter>  
</receiver>
<receiver android:name=".MyBroadcastReceiver2">  
    <intent-filter android:priority="99"> 
        <action android:name="android.intent.action.test"/>  
    </intent-filter>  
</receiver>
class MyBroadcastReceiver1 extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver1";
    @Override
    public void onReceive(Context context, Intent intent) {
		String action = intent.getAction();
        if (action.eq("android.intent.action.test")) {
        	String id = intent.getStringExtra("id");
            Log.d(TAG, "Receiver Broadcast: " + action + ", id = " + id);
            Bundle bundle = new Bundle();
            bundle.putString("data", "hello, world!");
            setResultExtras(bundle);
        }
    }
}
class MyBroadcastReceiver2 extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver2";
    @Override
    public void onReceive(Context context, Intent intent) {
		String action = intent.getAction();
        if (action.eq("android.intent.action.test")) {
        	String id = intent.getStringExtra("id");
            Log.d(TAG, "Receiver Broadcast: " + action + ", id = " + id);
            String data = getResultExtras(true).getString("data");
            Log.d(TAG, "getResultExtras data: " + data);
        }
    }
}

上面的例子中,MyBroadcastReceiver2即可接收的MyBroadcastReceiver1處理結果。

sendOrderedBroadcast(Intent intent, String receiverPermission)方法中的第二個參數用來設置接收者是否需要申請權限。如果參數爲null,則接收者不需要申請權限就可以接收廣播。如果不爲空則需要申請權限,否則接收不到廣播。

Intent intent = new Intent("android.intent.action.test");
intent.putExtra("id", 100);
sendOrderedBroadcast(intent, "test.permission.broadcast");

接收端需要申請權限纔可以接收到該廣播。

<permission android:protectionLevel="normal" android:name="test.permission.broadcast"/>
<uses-permission android:name="test.permission.broadcast" />

本地廣播

前面說到的廣播全都是屬於系統全局廣播,即發出的廣播後可以被其他應用接收到,而且也可以接收到其他應用發送出的廣播,這樣可能會有不安全因素。因此,在某些情況下可以採用本地廣播機制,使用這個機制發出的廣播只能在應用內部進行傳遞,而且廣播接收器也只能接收本應用內自身發出的廣播。

本地廣播擁有以下特點:

  1. 發送的廣播只會在當前APP中傳播,不會泄露給其它APP,確保了數據傳輸的安全。
  2. 其它APP的廣播無法發送到本APP中,不用擔心安全漏洞被其它APP所利用。
  3. 比系統全局廣播更加高效。

本地廣播是使用 LocalBroadcastManager 來對廣播進行管理,LocalBroadcastManager是support V4包中的一個組件。

函數 作用
LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver, IntentFilter) 註冊Receiver
LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver); 註銷Receiver
LocalBroadcastManager.getInstance(this).sendBroadcast(Intent) 發送異步廣播
LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent) 發送同步廣播

本地廣播的接收者與全局廣播是一樣的。只是註冊和發送的方式改變了而已。另外,本地廣播是無法通過靜態註冊的方式來接收的,因爲靜態註冊廣播主要是爲了在程序未啓動的情況下也能接收廣播,而本地廣播是應用自己發送的,此時應用肯定是啓動的了。

public class TestActivity extends Activity {
	private LocalBroadcastManager mLocalBroadcastManager;

    private LocalReceiver mLocalReceiver;

    private final String LOCAL_ACTION = "android.intent.action.local_test";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        mLocalReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        mLocalBroadcastManager.registerReceiver(mLocalReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocalBroadcastManager.unregisterReceiver(mLocalReceiver);
    }
    
    public void sendLocalBroadcast() {
        Intent intent = new Intent(LOCAL_ACTION);
        mLocalBroadcastManager.sendBroadcast(intent);
    }

    public static class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("LocalReceiver", "onReceive : " + intent.getAction());
        }
    }
}

生命週期

BroadcastReceiver 的生命週期與其他系統組件還是有些不一樣的。在 AndroidManifest.xml 中註冊的BroadcastReceiver , 每次收到一個 Intent , 也就是 onReceive 被回調的時候, 這個 BroadcastReceiver都是新創建出來的。也就是說, 出了 onReceive , 這個 BroadcastReceiver對象的生命週期就已經到頭了, 這也是爲什麼我們不能在 onReceive 中進行一些異步操作的原因, 有可能異步操作還沒完成, BroadcastReceiver所在的進程就被kill了。 所以,我們在接受到廣播後,需要進程執行一些異步任務是,需要配合 Service 來實現。

而動態註冊的 BroadcastReceiver 對象的生命其實是受我們控制的。既然是動態註冊,那麼我們的應用一定是處於運行狀態的。此時,只要該進程不被kill掉,被註冊的 BroadcastReceiver 都是在註冊指定的實例中處理接收的廣播數據的。

使用自定義權限

使用全局廣播接收器存在一個問題,即系統內的任何應用均可觸發我們的 Receiver 。通常情況下我們是不希望如此的。爲了避免這種問題,可以有以下方式:

  1. 使用本地廣播,但本地廣播只允許本應用自己發出;
  2. AndroidManifest.xml 清單文件中爲 < receiver > 標籤添加一個 android:exported=“false” 屬性,標明該 Receiver 僅限應用內部使用,這樣的結果一樣是無法接收到外部發來的廣播。
  3. 使用自定義權限來對接收者進行過濾,只有聲明瞭指定權限的 Receiver 纔可以接收到該廣播。

使用私有權限,首先需要在AndroidManifest.xml 清單文件中聲明自定義權限。

 <permission
        android:name="com.test.permission.receiver"
        android:protectionLevel="signature" />

聲明自定義權限時必須同時指定 protectionLevel 屬性值,系統根據該屬性值確定自定義權限的使用方式

屬性值 限定方式
normal 默認值。較低風險的權限,對其他應用,系統和用戶來說風險最小。系統在安裝應用時會自動批准授予應用該類型的權限,不要求用戶明確批准(雖然用戶在安裝之前總是可以選擇查看這些權限)
dangerous 較高風險的權限,請求該類型權限的應用程序會訪問用戶私有數據或對設備進行控制,從而可能對用戶造成負面影響。因爲這種類型的許可引入了潛在風險,所以系統可能不會自動將其授予請求的應用。例如,系統可以向用戶顯示由應用請求的任何危險許可,並且在繼續之前需要確認,或者可以採取一些其他方法來避免用戶自動允許
signature 只有在請求該權限的應用與聲明權限的應用使用相同的證書籤名時,系統纔會授予權限。如果證書匹配,系統會自動授予權限而不通知用戶或要求用戶的明確批准
signatureOrSystem 系統僅授予Android系統映像中與聲明權限的應用使用相同的證書籤名的應用。請避免使用此選項,“signature”級別足以滿足大多數需求,“signatureOrSystem”權限用於某些特殊情況

新建一個工程作爲發送端,在它的 AndroidManifest.xml 文件中創建一個自定義權限,並聲明該權限。

<permission android:name="com.test.permission.receiver" android:protectionLevel="signature" /> 
<uses-permission android:name="com.test.permission.receiver" />

發送含有該權限聲明的 Broadcast,這樣,只有使用相同證書籤名且聲明該權限的應用才能接收到該 Broadcast。

private final String PRIVATE_PERMISSION = "com.test.permission.receiver";
public void sendPermissionBroadcast(View view) {
    Intent intent = new Intent("android.intent.action.test");
    sendBroadcast(intent, PRIVATE_PERMISSION);
}

而接收端,則只需要在AndroidManifest.xml 文件中也聲明該權限,就可以接收到該廣播了。

<uses-permission android:name="com.test.permission.receiver" />

接收端註冊廣播:

private final String PRIVATE_PERMISSION = "com.test.permission.receiver";
private final String ACTION = "android.intent.action.test";
IntentFilter intentFilter1 = new IntentFilter(ACTION); 
MyBroadcastReceiver receiver = new MyBroadcastReceiver(); 
registerReceiver(receiver, intentFilter1, PRIVATE_PERMISSION, null);

Android常用系統廣播

廣播名 說明 備註
Intent.ACTION_BATTERY_LO 電池電量低
Intent.ACTION_BATTERY_OK 電池電量充足
Intent.ACTION_AIRPLANE_MODE_CHANGED 關閉或打開飛行模式
Intent.ACTION_BATTERY_CHANGED 充電狀態,或者電池的電量發生變化 電荷級別改變,只能在代碼註冊
Intent.ACTION_BATTERY_LOW 電池電量低
Intent.ACTION_BATTERY_OKAY 電池電量充足 從電池電量低變化到飽滿時會發出廣播
Intent.ACTION_BOOT_COMPLETED 在系統啓動完成後,這個動作被廣播一次 只有一次
Intent.ACTION_CAMERA_BUTTON 按下照相時的拍照按鍵時發出的廣播 硬件按鍵
Intent.ACTION_CLOSE_SYSTEM_DIALOGS 當屏幕超時進行鎖屏時,當用戶按下電源按鈕,長按或短按(不管有沒跳出話框),進行鎖屏
Intent.ACTION_CONFIGURATION_CHANGED 設備當前設置被改變時發出的廣播 界面語言,設備方向,等 請參考Configuration.java
Intent.ACTION_DATE_CHANGED 設備日期發生改變時
Intent.ACTION_HEADSET_PLUG 在耳機口上插入耳機時發出的廣播
Intent.ACTION_INPUT_METHOD_CHANGED 改變輸入法時發出的廣播
Intent.ACTION_LOCALE_CHANGED 設備當前區域設置已更改時發出的廣播
Intent.ACTION_MANAGE_PACKAGE_STORAGE 表示用戶和包管理所承認的低內存狀態通知應該開始
Intent.ACTION_MEDIA_BAD_REMOVAL 未正確移除SD卡 擴展卡已經從SD卡插槽拔出,但是掛載點 (mount point) 還沒解除 (unmount)
Intent.ACTION_MEDIA_CHECKING 插入外部儲存裝置 比如SD卡時,系統會檢驗SD卡,此時發出的廣播
Intent.ACTION_MEDIA_EJECT 已拔掉外部大容量儲存設備發出的廣播 不管有沒有正確卸載
Intent.ACTION_MEDIA_MOUNTED 插入SD卡並且已正確安裝 擴展介質被插入而且已經被掛載
Intent.ACTION_MEDIA_NOFS 拓展介質存在,但使用不兼容FS(或爲空)的路徑安裝點檢查介質包含在Intent.mData領域
Intent.ACTION_MEDIA_REMOVED 外部儲存設備已被移除,擴展介質被移除 不管有沒正確卸載,都會發出此廣播
Intent.ACTION_MEDIA_SCANNER_FINISHED 已經掃描完介質的一個目錄
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE 請求媒體掃描儀掃描文件並將其添加到媒體數據庫
Intent.ACTION_MEDIA_SCANNER_STARTED 開始掃描介質的一個目錄
Intent.ACTION_MEDIA_SHARED 擴展介質的掛載被解除 (unmount) 它已經作爲 USB 大容量存儲被共享
Intent.ACTION_PACKAGE_ADDED 成功的安裝APK 數據包括包名(最新安裝的包程序不能接收到這個廣播)
Intent.ACTION_PACKAGE_CHANGED 一個已存在的應用程序包已經改變 包括包名
Intent.ACTION_PACKAGE_DATA_CLEARED 清除一個應用程序的數據時發出的廣播 清除包程序不能接收到這個廣播
Intent.ACTION_PACKAGE_INSTALL 觸發一個下載並且完成安裝時發出的廣播 比如在電子市場裏下載應用
Intent.ACTION_PACKAGE_REMOVED 成功的刪除某個APK之後發出的廣播 正在被安裝的包程序不能接收到這個廣播
Intent.ACTION_PACKAGE_REPLACED 替換一個現有的安裝包時發出的廣播(不管現在安裝的APP比之前的新還是舊
Intent.ACTION_PACKAGE_RESTARTED 用戶重新開始一個包 重新開始包程序不能接收到這個廣播
Intent.ACTION_POWER_CONNECTED 插上外部電源時發出的廣播
Intent.ACTION_POWER_DISCONNECTED 已斷開外部電源連接時發出的廣播
Intent.ACTION_REBOOT 重啓設備時的廣播
Intent.ACTION_SCREEN_OFF 屏幕被關閉之後的廣播
Intent.ACTION_SCREEN_ON 屏幕被打開之後的廣播
Intent.ACTION_SHUTDOWN 關閉系統時發出的廣播
Intent.ACTION_TIMEZONE_CHANGED 時區發生改變時發出的廣播
Intent.ACTION_TIME_CHANGED 時間被設置時發出的廣播
Intent.ACTION_TIME_TICK 當前時間已經變化(正常的時間流逝) 每分鐘都發送,在代碼註冊
Intent.ACTION_UID_REMOVED 一個用戶ID已經從系統中移除發出的廣播
Intent.ACTION_UMS_CONNECTED 設備已進入USB大容量儲存狀態時發出的廣播
Intent.ACTION_UMS_DISCONNECTED 設備已從USB大容量儲存狀態轉爲正常狀態時發出的廣播
Intent.ACTION_WALLPAPER_CHANGED 設備牆紙已改變時發出的廣播
Intent.ACTION_USER_PRESENT 用戶喚醒設備
Intent.ACTION_NEW_OUTGOING_CALL 撥打電話
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章