1. 程式人生 > >新人必看!安卓廣播的實現

新人必看!安卓廣播的實現

一、概述

廣播(Broadcast)機制用於程序/執行緒間通訊,因此在我們應用程式內發出的廣播,其他的應用程式應該也是可以收到的。廣播分為廣播發送和廣播接收兩個過程,其中廣播接收者BroadcastReceiver便是Android四大元件之一。

BroadcastReceiver(廣播接收)分為兩類:

  • 靜態廣播接收者:通過AndroidManifest.xml的標籤來申明的BroadcastReceiver。
  • 動態廣播接收者:通過AMS.registerReceiver()方式註冊的BroadcastReceiver,動態註冊更為靈活,可在不需要時通過unregisterReceiver()取消註冊。

廣播發送方式可分為三類:

  • 普通廣播:通過Context.sendBroadcast()傳送,可並行處理
  • 有序廣播:通過Context.sendOrderedBroadcast()傳送,序列處理
  • Sticky廣播:通過Context.sendStickyBroadcast()傳送

二、作用

主要用於監聽(接收)應用發出的廣播訊息,並做出響應。

常見應用場景 

a. 不同元件之間通訊(包括應用內 / 不同應用之間) 

b. Android系統在特定情況下與App之間的訊息通訊(當電話呼入時,網路可用時)

c. 多執行緒通訊

三、註冊廣播

1.靜態註冊廣播

常駐型廣播,這個廣播接收者會在程式執行的整個過程中一直存在,不會被登出掉,當程式被殺掉後不會再接收到廣播了。它的註冊方式就是在你應用程式的AndroidManifast.xml 中進行註冊,這種註冊方式通常又被稱作靜態註冊。這種方式可以理解為通過清單檔案註冊的廣播是交給作業系統去處理的。在Manifest.xml中註冊廣播,是一種比較推薦的方法,因為它不需要手動登出廣播(如果廣播未登出,程式退出時可能會出錯)在配置檔案中加入:

<receiver android:name=".receiver.WakeReceiver">
    <intent-filter>
        <!--   利用系統廣播拉活-->
        <action android:name="android.intent.action.BOOT_COMPLETED" /> <!-- Android開機廣播-->
        <!-- 系統廣播,接收打電話的廣播 ,這個需要配置許可權 -->
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        <action android:name="android.intent.action.USER_PRESENT" /><!--鎖屏解鎖-->
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /><!--網路變化-->
        <action android:name="com.wake.gray" /><!-- 自定義廣播 -->
    </intent-filter>
</receiver>

2.動態註冊廣播

動態註冊廣播不是常駐型廣播,也就是說廣播跟隨程式的生命週期結束。

實現流程:

  1. 例項化自定義的廣播接收者
  1. 例項化意圖過濾器,並設定要過濾的廣播型別(如,我們接收收到簡訊系統發出的廣播)
  1. 使用Context的registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)方法註冊廣播

3.兩種註冊廣播的區別

第一種是常駐型,也就是說當應用程式關閉後,如果有資訊廣播來,程式也會被系統呼叫自動執行,比如靜態註冊實現開機啟動。

第二種不是常駐型廣播,也就是說廣播跟隨程式的生命週期。動態註冊的廣播的優先順序高於靜態註冊的廣播。

四、 使用流程介紹

  1. 自定義廣播接收者BroadcastReceiver子類,並複寫onRecvice()方法;
  1. 通過Binder機制向AMS(Activity Manager Service)進行註冊;
  1. 廣播發送者通過Binder機制向AMS傳送廣播;
  1. AMS查詢符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(一般情況下是Activity)相應的訊息迴圈佇列中;
  1. 訊息迴圈執行拿到此廣播,回撥BroadcastReceiver中的onReceive()方法。

4.1 自定義廣播接收者

  • 繼承自BroadcastReceivre基類
  • 必須複寫抽象方法onReceive()如下:

1、廣播接收器接收到相應廣播後,會自動回撥onReceive()方法

2、一般情況下,onReceive方法會涉及與其他元件之間的互動,如傳送Notification、啟動service等

3、預設情況下,廣播接收器執行在UI執行緒,因此,onReceive方法不能執行耗時操作,否則將導致ANR

/**
 *  步驟一:繼承BroadcastReceiver基類
 *  自定義接受網路變化的廣播接收器
 *  該廣播接收器可以單獨寫一個外部類
 * */
   class NetworkChangeReceiver extends BroadcastReceiver {
         // 接收到廣播後自動呼叫該方法
         @Override
   public void onReceive(Context context, Intent intent) {
             //寫入接收廣播後的操作
             ConnectivityManager connectionManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
             if (networkInfo != null && networkInfo.isAvailable()) {
                 Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
             } else {
                 Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
             }
         }
     }

4.2 廣播接收器註冊

  1. 註冊的方式分為兩種:靜態註冊、動態註冊

靜態註冊:

  • 在AndroidManifest.xml裡通過標籤宣告
  • 屬性說明:
<receiver
    android:name=".MyBroadcastReciver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <!--<action android:name="android.intent.action.BOOT_COMPLETED"/>-->
        <action android:name="com.example.broadcassttest.MY_BROADCAST"/>
    </intent-filter>
    </receiver>

當此App首次啟動時,系統會自動例項化mBroadcastReceiver類,並註冊到系統中。

動態註冊:

在程式碼中通過呼叫Context的registerReceiver()方法進行動態註冊BroadcastReceiver

registerReceiver(networkChangeReceiver, intentFilter); // 動態註冊

注意:動態註冊最好在onResume()註冊、onPause()登出,是因為onPause()在App死亡前一定會被執行,從而保證廣播在App死亡前一定會被登出,從而防止記憶體洩露。

  1. 兩種註冊方式對比

4.3 廣播發送者向AMS傳送廣播

  • 廣播發送:廣播發送者將此廣播的”意圖“通過sendBroadcast()方法傳送出去
//動態接受網路變化的廣播接收器
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

五、程式實現

MyBroadcastReciver.java

public class MyBroadcastReciver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"自定義廣播接收者",Toast.LENGTH_LONG).show();
    }
}

Mainactivity.java

/**
 * 動態註冊案例
 */
public class MainActivity extends AppCompatActivity {
    private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;
    private Button noticebtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        noticebtn = (Button)findViewById(R.id.notice_btn);
        //動態接受網路變化的廣播接收器
        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(networkChangeReceiver, intentFilter); // 動態註冊
        setOnclick();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //取消動態網路變化廣播接收器的註冊
        unregisterReceiver(networkChangeReceiver);
    }
    // 建立廣播
//    class NetworkChangeReciver extends BroadcastReceiver{
//        @Override
//        public void onReceive(Context context, Intent intent) {
//            Toast.makeText(context,"網路改變",Toast.LENGTH_LONG);
//        }
//    }
    /**
     *  步驟一:繼承BroadcastReceiver基類
     *  自定義接受網路變化的廣播接收器
     *  該廣播接收器可以單獨寫一個外部類
     * */
         class NetworkChangeReceiver extends BroadcastReceiver {
             // 一般情況下,onReceive方法會涉及與其他元件之間的互動,如傳送Notification、啟動service等
             // 預設情況下,廣播接收器執行在UI執行緒,因此,onReceive方法不能執行耗時操作,否則將導致ANR
             // 接收到廣播後自動呼叫該方法
             @Override
             public void onReceive(Context context, Intent intent) {
                 //寫入接收廣播後的操作
                 ConnectivityManager connectionManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
                 NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
                 if (networkInfo != null && networkInfo.isAvailable()) {
                     Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
                 } else {
                     Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
                 }
             }
         }
         private void setOnclick(){
             noticebtn.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View view) {
                     //  傳入需要傳送的廣播
                     Intent intent = new Intent("com.example.broadcassttest.MY_BROADCAST");
                     sendBroadcast(intent);  // 將廣播發送出去
                 }
             });
         }
}

參考學習: