Android四大組件之BroadcastReceiver

什麼是BroadcastReceiver

BroadcastReceiver是安卓中的四大組件之一。廣播接收器,也被稱爲全局事件,或系統事件。
當Android系統中任何程序有動作時,如果想通知其他程序,採用廣播的方式進行傳播是非常有效的。廣播從理論上說,可以將一個動作傳播給任意多個程序(當然,廣播接收器的數量會受到系統限制)。
在Android中,有一些操作完成以後,會發送廣播,比如說發出一條短信,或打出一個電話,如果某個程序接收了這個廣播,就會做相應的處理。這個廣播跟我們傳統意義中的電臺廣播有些相似之處。之所以叫做廣播,就是因爲它只負責“說”而不管你“聽不聽”,也就是不管你接收方如何處理。另外,廣播可以被不只一個應用程序所接收,當然也可能不被任何應用程序所接收。
廣播機制最大的特點就是發送方並不關心接收方是否接到數據,也不關心接收方是如何處理數據的。
Android中廣播的是操作系統中產生的各種各樣的事件。例如,收到一條短信就會產生一個收到短信息的事件。而Android操作系統一旦內部產生了這些事件,就會向所有的廣播接收器對象來廣播這些事件。

廣播機制的三要素:

1、廣播(Broadcast) - 用於發送廣播;
2、廣播接收器(BroadcastReceiver) - 用於接收廣播;
3、意圖內容(Intent)-用於保存廣播相關信息的媒介。
Broadcast是一種廣泛運用的在應用程序之間傳輸信息的機制。而BroadcastReceiver是對發送出來的Broadcast進行過濾接受並響應的一類組件

廣播的生命週期:

1、廣播接收器僅在它執行這個方法時處於活躍狀態。當onReceive()返回後,它即爲失活狀態。
2、擁有一個活躍狀態的廣播接收器的進程被保護起來而不會被殺死,但僅擁有失活狀態組件的進程則會在其它進程需要它所佔有的內存的時候隨時被殺掉。
3、如果響應一個廣播信息需要很長的一段時間,一般會將其納入一個衍生的線程中去完成,而不是在主線程內完成它,從而保證用戶交互過程的流暢。廣播接收程序的時間限制爲10秒。

BroadcastReceiver註冊類型

  • 靜態註冊
靜態註冊主要是爲了能讓程序在未啓動的時候也能接收廣播。
清單文件中代碼:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.broadcastreceiverdemo">
    <!--電話監聽權限-->
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>
        <!--1、廣播接收者的類名建議使用全名(包名.類名)
            2、意圖過濾器中action是指的當前action能接受的廣播類型(靜態註冊必須註明Action)。
            3、可以註冊的時候給意圖過濾器添加屬性android:priority="100"來說明該廣播的優先級。當接受有序廣播時,優先級越高越先收到廣播。-->
        <receiver
            android:name="com.broadcastreceiverdemo.MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true"
            android:priority="1000">
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            <action android:name="android.intent.action.PHONE_STATE"/>
        </receiver>
    </application>

</manifest>
自定義BroadcastReceiver中的代碼:
package com.broadcastreceiverdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

/**
 * Created by YK on 2016/8/30.
 */
public class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "dy";

    /**
     * 該方法內部寫接收到廣播之後的邏輯處理代碼。
     * 此方法是在UI線程中執行的,所以不能執行耗時操作,10s內還未響應則報ANR。如果需要執行耗時操作,則可以啓動子線程來執行。
     * @param context
     * @param intent  數據都封裝在intent對象中,可以從此對象中取出需要的數據
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){
            String phoneNum = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            Log.e(TAG, "onReceive: "+phoneNum);
            Toast.makeText(context, "撥打的電話號碼是:" + phoneNum, Toast.LENGTH_SHORT).show();
        }else {
            Log.e(TAG, "onReceive: ");
        }
    }
}
此處監聽的系統撥打電話,我用的是小米4C,發現並沒有監聽到撥打電話,程序是沒問題的,後來我查了網上一些資料,我這手頭上暫時沒有別的手機,朋友們可以直接複製上面的代碼自己來測試一下,以後我用別的手機測試沒問題會截圖說明。
  • 動態註冊
BroadcastReceiver是四大組件中唯一支持使用java代碼進行註冊的組件(動態註冊),所以在AndroidManifest文件中的註冊也可以使用java代碼註冊來替換。動態註冊的方式需要先開啓應用才能接收到要接收的廣播,仍然採用上面的例子,MainActivity中的代碼:
package com.broadcastreceiverdemo;

import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn;
    private MyBroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn= (Button) findViewById(R.id.btn);
        btn.setOnClickListener(this);
    }

    /**
     * 點擊按鈕註冊廣播接收者
     * @param v
     */
    @Override
    public void onClick(View v) {
        receiver = new MyBroadcastReceiver();
        IntentFilter filter=new IntentFilter();
        filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);//添加監聽的Action
        registerReceiver(receiver,filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(receiver!=null){
            unregisterReceiver(receiver);
        }
    }
}
MyBroadcastReceiver中的代碼和前面的一樣,撥打10086測試結果如下:

靜態註冊和動態註冊的區別:

1、靜態註冊的廣播接收者app一旦安裝在系統,則可以接收到指定的廣播。不管這個app有沒有啓動。 2、動態註冊的廣播接收者只有在註冊完成後才能接收到廣播,當註冊他的組件銷燬的時候,廣播接收者也應該解除註冊。

發送廣播

廣播的發送和接收是跨進程的,一個app發送的廣播,其他的app只要權限和action匹配都可以接收到該廣播,注:發送廣播的方法都是上下文對象中的方法
  • 發送標準廣播
//創建意圖對象,並指明action,那麼意圖過濾器與這個action匹配的廣播接受者會
//接收到這個廣播
Intent intent = new Intent("com.qianfeng.MY_BROADCAST");  
//發送出去廣播
sendBroadcast(intent);
  • 發送有序廣播
Intent intent = new Intent("com.qianfeng.MY_BROADCAST");  
 
//發送有序廣播。  參數一:意圖對象  參數二:權限。是否需要接受者需要選取纔可以收到廣播
sendOrderedBroadcast(intent, null);
或者:
//由於發送有序廣播的時候,中間會有可能被攔截掉,參數三則指定了一個這個廣播
//的最終接受者,也就說即使中間有人攔截了廣播,則參數三指定的接
//受者也會最終接收到這個廣播。
//其餘的參數給null或0即可
/**參數一:意圖對象,發送什麼樣的廣播
 * 參數二:權限  一般不需要
 * 參數三:是最後一個收到廣播的接受者,他可以不用註冊,也能收到
 * 參數四:指明最終的接受者執行的線程(如果Handler對象時在子線程創建則handMessage方法是在子線程執行)。傳null,怎默認是在主線程執行
 * 參數五:初始化的code
 * 參數六:初始化的數據
 * 參數七:傳遞比較複雜的數據可以使用Bundle對象
 * 
 * 最終的接收者收到的數據是有可能被中間的接收者篡改
 */
sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
注意:
1、發送有序廣播的時候,先接到廣播的可以通過下面的方法來取消廣播,導致後面的優先級低的廣播接收者收不到廣播。
abortBroadcast(); //放棄當前的廣播,則優先級低的無法收到當前廣播
2、如果優先級高的廣播接收者想給優先級低的廣播接收者傳遞數據可以通過下面的方法:
setResult()
setResultData()
setResultCode()
setResultExtra()
對方用getResultData()獲得數據

上述兩種廣播的圖解說明:

  • 標準廣播
標準廣播(Normal broadcasts)是一種完全異步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們之間沒有任何先後順序可言。這種廣播的效率會比較高,但同時也意味着它是無法被截斷的。

  • 有序廣播
有序廣播(Ordered broadcasts)則是一種同步執行的廣播,在廣播發出之後,同一時刻只會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播纔會繼續傳遞。所以此時的廣播接收器是有先後順序的,優先級高的廣播接收器就可以先收到廣播消息,並且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣後面的廣播接收器就無法收到廣播消息了,甚至前面的廣播可以修改廣播內容再傳遞給下一個廣播。

本地廣播LocalBroadcastReceiver

LocalBroadcastManager是Android Support包提供了一個工具,是用來在同一個應用內的不同組件間發送Broadcast的
使用LocalBroadcastManager有如下好處:
發送的廣播只會在自己App內傳播,不會泄露給其他App,確保隱私數據不會泄露;其他App也無法向你的App發送該廣播,不用擔心其他App會來搞破壞;比系統全局廣播更加高效;和系統廣播使用方式類似:
MainActivity中代碼:
package com.broadcastreceiverdemo;

import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn,btn2;
    private MyBroadcastReceiver receiver;
    private LocalBroadcastManager localBroadcastManager;
    public static String BROAD_CAST="my broadcast";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        btn= (Button) findViewById(R.id.btn);
        btn2= (Button) findViewById(R.id.btn2);
        btn.setOnClickListener(this);
        btn2.setOnClickListener(this);
    }

    /**
     * 點擊按鈕註冊廣播接收者/發送廣播
     * @param v
     */
    @Override
    public void onClick(View v) {
        if(v.getId()==R.id.btn){
            receiver=new MyBroadcastReceiver();
            IntentFilter filter=new IntentFilter();
            filter.addAction(BROAD_CAST);//只接收該Action的廣播
            localBroadcastManager.registerReceiver(receiver,filter);
        }else if(v.getId()==R.id.btn2){//發送自定義Action的廣播
            Intent intent=new Intent(BROAD_CAST);
            intent.putExtra("kaka","哈哈");
            localBroadcastManager.sendBroadcast(intent);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(receiver!=null){
            localBroadcastManager.unregisterReceiver(receiver);
        }
    }
}
MyBroadcastReceiver中代碼:
package com.broadcastreceiverdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

/**
 * Created by YK on 2016/8/30.
 */
public class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "dy";

    /**
     * 該方法內部寫接收到廣播之後的邏輯處理代碼。
     * 此方法是在UI線程中執行的,所以不能執行耗時操作,10s內還未響應則報ANR。如果需要執行耗時操作,則可以啓動子線程來執行。
     * @param context
     * @param intent  數據都封裝在intent對象中,可以從此對象中取出需要的數據
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals(MainActivity.BROAD_CAST)){
            Log.e(TAG, "onReceive: "+intent.getStringExtra("kaka"));
        }
    }
}
先點擊Register BroadcastReceiver按鈕,再點擊send Broadcast按鈕,打印的log信息:


LocalBroadcastReceiver代碼下載:源碼

廣播接收案例(接收一些系統廣播)

系統在合適的時間發送廣播,我們的app通過接收這些廣播可以實現一些比較實用的功能。如:系統啓動完成、用戶外撥電話、短信來到等等,系統都會發出相應的廣播。
ACTION_TIME_CHANGED :系統時間被改變;
ACTION_DATE_CHANGED : 系統日期被改變;
ACTION_TIMEZONE_CHANGED :系統時區被改變;
ACTION_BOOT_COMPLETED :系統啓動完成;
ACTION_BATTERY_CHANGED : 電池電量改變;
ACTION_SHUTDOWN : 系統被關閉;
Action_BATTRY_LOW : 電池電量低;

廣播接收者的一些簡單應用:

1.軟件自啓
2.攔截外撥電話
3.短信攔截
4.網絡、Wifi狀態的訪問等等。
參考資料:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章