Now you can Subscribe using RSS

Submit your Email

2017/07/09

Android Broadcast

asd
廣播為 Android 四大組件之一。
可以接收系統或是 APP 發送的訊息做相對應的動作。
在廣播中無法處理太繁瑣費時的動做因為所在線程為主線程,
處理過久會產生 ANR 情況。

盡量不要開子線程進行處理,BroadcastReceiver 生命周期短,
假設子線程處理未結束廣播關閉又剛好系統需要資源有可能會回收廣播,
那子線程也會跟著一起被結束。

如需做複雜資訊處理耗時過久可以透過 Intent 發送給 Service 幫你處理。

廣播可以攔截到的資訊分為兩類。
  • 系統廣播 : 電量變化、網路開關、GPS開關等等。
  • 自定義廣播 : 自定義的 intent filter action 。

廣播也有兩種註冊方式
  • 動態註冊 : 在 Class 中註冊。
  • 靜態註冊 : 在 Manifest 中註冊。

發送廣播類型
  • Normal Broadcast : 標準廣播任何人都可以收到該廣播。
  • Ordered Broadcast : 順序廣播依照權限順序接續發送也可中斷廣播。
  • Local Broadcast : 本地廣播只有該 APP 可以接收其他人無發接收。

動態註冊


以監聽系統中 GPS 開關作為範例。
首先寫一個 class 然後繼承 BroadcastReceiver 程式碼如下。
private static final String GPS = "android.location.PROVIDERS_CHANGED";

class GPSReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "onReceive");
            switch (intent.getAction()) {
                case GPS:
                    LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
                    //衛星定位
                    boolean isGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
                    //網路定位
                    boolean isNetWork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
                    tvGps.setText(++gpsNum + " 次 isOpen = " + (isGps || isNetWork));
                    break;
            }
        }
    }
String GPS 為 android 系統如果發生 GPS 開關狀態改變時會發送的廣播。
我們必須要攔截這個廣播才可以做相對應的動作。
GPS 有兩種模式一種是吃衛星定位另一種是網路定位。
所以我們在 onReceiver 中判斷狀態看看 GPS 是否開啟。

寫好 onReceiver 後我們在 Activity 中註冊廣播。
廣播的註冊前必須先使用 intentFilter 去加入我們要攔截的廣播 Action。
這樣你的 receiver 才會接收到 Action。
private GPSReceiver mGPSReceiver;
private int gpsNum = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tvGps = (TextView) findViewById(R.id.tv_GPS);
        
        //加入需要攔截的廣播 Action
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(GPS);

        //廣播註冊
        mGPSReceiver = new GPSReceiver();
        registerReceiver(mGPSReceiver, intentFilter);
}

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //反註冊
        unregisterReceiver(mGPSReceiver);
    }
有註冊當然也要反註冊。
通常會搭配 Activity 的生命週期進行註冊與反註冊。
效果如下。

靜態註冊

以同樣的 GPS 開關為例子。
使用方式新建一個 class 然後繼承 BroadcastReceiver。
或是在你想要放置的 class package 點擊右鍵 new > Other > Broadcast Receiver 。

然後看到以下畫面。


Exported : 代表是否接收此程式以外的廣播。
Enabled : 代表是否請用該 Broadcast Receiver。

建立完之後看一下 manifest 檔。
如果是使用 Android studio 幫你建立的會出現類似這樣的畫面。
可以發現到在 application tag 中會多出一個 receiver tag 這就是靜態註冊的方式。
name : 對應到該使用的 Broadcast Receiver class,因為我放在 Receiver 目錄下所以出現 Receiver.MyStaticReceiver。
enable 與 exported 就是剛剛建立時可以勾選的設定。

receiver tag 中又包含了 intent-filter tag  。
priority : 聲明 Broadcast Receiver 的優先權數字越小越優先接收到廣播。

intent-filter tag 中包含了 action tag 這裡是聲明要攔截的廣播。
我們一樣的把剛剛的 String GPS 的 action 寫進去 。

※假如是手動建立 Class  extends Broadcast 就需要手動加入 manifest 註冊。

這是 MainActivity.class
我們裡面什麼都不寫因為使用靜態註冊。
 public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
} 
這是 MyStaticReceiver.class。
 public class MyStaticReceiver extends BroadcastReceiver {
    private static final String GPS = "android.location.PROVIDERS_CHANGED";
    public static final String MY_BROADCAST = "com.demo.myBroadcast";
    public static final String LOCAL = "com.demo.local";

    private int gpsNum = 0;
    private int myBroadcastNum = 0;

    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getAction()) {
            case MY_BROADCAST:
                Toast.makeText(context, "Static My Broadcast =" + (++myBroadcastNum) + "次 intent bundle string " +
                                "=" + intent.getExtras().getString("Test"),
                        Toast.LENGTH_SHORT).show();
                break;
            case GPS:
                LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
                //衛星定位
                boolean isGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
                //網路定位
                boolean isNetWork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

                Toast.makeText(context, "Static GPS Broadcast =" + (++gpsNum) + "次 isOpen=" + (isGps || isNetWork),
                        Toast
                                .LENGTH_SHORT)
                        .show();
                break;
            case LOCAL:
                Toast.makeText(context, "static Local", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

因為後面還會繼續介紹其他東西所以目前先看 switch case 中的 GPS 就可以了。
裡面一樣做了網路與衛星定位的判斷。
使用 Toast 顯示資訊。
然後同時把動態註冊與靜態註冊的 APP 一起安裝到手機中然後開關 GPS 看看效果。
首先可以看到沒有啟動靜態註冊的 APP 然後開啟動態註冊 APP且切換 GPS 。
可以看到未開啟的靜態註冊 APP 也可以攔截到 GPS開關。
且顯示 TOAST 出來。

以下是不開啟動態註冊 APP 直接開關 GPS。
可以看到靜態註冊的 receiver 一樣可以攔截到廣播。
但動態註冊的無法。

1 意見:

Coprights @ 2016, Blogger Templates Designed By Templateism | Templatelib