文章目錄
- 廣播概念
- 接收廣播
- 動態注冊
- 實例
- 靜態注冊
- 實例
- 發送廣播
- 發送標準廣播
- 廣播的跨進程特性
- 發送有序廣播
- 本地廣播
廣播概念
Android 中的每個應用程序都可以對自己感興趣的廣播進行注冊,這樣該程序就只會接收到自己所關心的廣播內容,這些廣播可能是來自系統的,也可能是來自于其他應用程序的。
廣播有兩種類型——有序廣播和標準廣播:
- 標準廣播: 一種完全異步執行的廣播,在廣播發出去之后,所有的廣播接收器幾乎都會同一時刻接收到這條廣播消息,沒有任何的先后順序可言,這種廣播的效率比較高,但是無法被截斷。
- 有序廣播: 是一種同步執行的廣播,在廣播發出去之后,同一時刻只會有一個廣播接收器能夠收到這條消息,當這個廣播接收器中的邏輯執行完畢之后,廣播才會繼續傳遞,所以這時候的廣播接收器是有優先級順序的,并且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣后面的廣播就無法收到廣播消息。
接收廣播
動態注冊
Android內置了很多系統級別的廣播,我們可以在應用程序中通過監聽這些廣播來得到各種系統的狀態信息。比如手機開機、電池電量發生變化、時間或者時區發生改變等等。如果想要接收到這些廣播就需要使用廣播接收器。
注冊廣播的方式一般也有兩種,在 代碼中注冊(動態注冊) 或者 在 AndroidManifest.xml
中注冊(靜態注冊)。
實例
我們實現一個能準確地告訴用戶當前有沒有網絡的功能,在實現代碼前,由于 Android 為了保護用戶設備的隱私和安全,規定了程序需要進行一些對用戶來說比較敏感的操作,必須在配置文件中聲明權限才可以,該功能監聽了網絡的變化,所以必須在 AndroidManifest.xml
配置權限才能訪問系統網絡狀態:
public class BroadcastActivity extends AppCompatActivity {private IntentFilter intentFilter; // 意圖過濾器,旨在匹配可以響應對應操作的組件private NetworkChangeReceiver networkChangeReceiver; // 自定義的廣播接收器類@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);intentFilter = new IntentFilter();// android.net.conn.CONNECTIVITY_CHANGE是網絡狀態變化時系統的廣播intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");networkChangeReceiver = new NetworkChangeReceiver();// 注冊,將接收器與IntentFilter相匹配registerReceiver(networkChangeReceiver, intentFilter);}// 動態注冊一定要在結束時取消注冊protected void onDestroy(){super.onDestroy();// 實現取消注冊unregisterReceiver(networkChangeReceiver);}// 自定義的廣播接收器類class NetworkChangeReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// 通過getSystemService得到管理網絡連接的connectivityManager實例ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();// 通過isAvailable方法判斷是否有網絡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();}}}
}
靜態注冊
動態注冊的廣播可以自由控制注冊、注銷,很靈活,缺點是必須在程序啟動之后才能接受到廣播,因為注冊的邏輯是寫在 onCreate()
里面的,使用靜態注冊可以讓程序在未啟動的情況下接受到廣播。
實例
靜態注冊需要在 AndroidManifest.xml
進行注冊,使用 receiver
標簽,并告訴這個 receiver
注冊哪一個 action
,下面是一個開機啟動接受廣播的案例:
public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// TODO: This method is called when the BroadcastReceiver is receiving// an Intent broadcast.Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();}
}
PS:不要在 onReceive()
方法中添加過多邏輯或耗時操作,因為廣播接收器中不允許開啟線程,因此當 onReceive()
運行較長時間卻未結束時,程序就會報錯。
發送廣播
發送標準廣播
發送廣播使用 Intent 進行發送,首先需要準備一個接收器用于接受發送的廣播:
public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 收到自定義廣播時會彈出提示Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_LONG).show();}
}
在 AndroidManifest.xml
中注冊廣播的值:
實現點擊 BroadcastActivity
活動中的 send broadcast
按鈕來發送廣播:
布局文件 broad_layout.xml
:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/button_broadcast1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="send broadcast"/>
</LinearLayout>
活動代碼 BroadcastActivity.java
:
public class BroadcastActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.broad_layout);Button button = findViewById(R.id.button_broadcast1);button.setOnClickListener((View v)->{// 將要發送的廣播植入IntentIntent intent = new Intent("com.example.activitytest.Activity.MY_BROADCAST");// 參數1:包名;參數2:接收器的路徑ComponentName componentName = new ComponentName("com.example.activitytest","com.example.activitytest.Activity.MyBroadcastReceiver");// 通過調用Intent中的setComponent方法,我們可以打開另外一個應用中的Activity或者服務。intent.setComponent(componentName);// 調用Context的sendBroadcast方法發送廣播sendBroadcast(intent);});}
}
PS: ComponentName
構造函數的第一個參數指的包名是 AndroidManifest.xml
文件下 package
屬性對應的包名:
而非 BroadcastActivity.java
文件所在的包名:
詳見該博客
運行結果:
廣播的跨進程特性
新建一個項目,創建廣播接收器 MyReceiver.java
:
public class MyReceiver extends BroadcastReceiver {private static final String TAG = "MyReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.e(TAG, "onReceive: gone");Toast.makeText(context, "received gone", Toast.LENGTH_LONG).show();}
}
AndroidManifest.xml
:
在原來項目的 BroadcastActivity.java
文件中發送第二條廣播:
運行結果:
更多關于廣播的問題詳見本文——解決 Android 8.0 以上靜態廣播無法注冊
發送有序廣播
很多人對之前的代碼可能會有疑問,我指定廣播發送給哪個包的哪個接收器,這還算“廣播”嗎?因此,對于安卓高版本而言,還有另一種發送廣播的方法:
修改 BroadcastActivity.java
中的代碼:
即可實現真正意義上的廣播。在此基礎上,我們發送有序廣播,定義接收器的優先級:
并在接收器 MyBroadcastReceiver.java
中截斷廣播,不允許廣播繼續傳遞:
public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 收到自定義廣播時會彈出提示Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();// 截斷abortBroadcast();}
}
不必對另一個接收器 MyReceiver 進行更改,此時就已達到了只有 MyBroadcastReceiver 能收到廣播,而 MyReceiver 不能收到廣播的目的。
本地廣播
前面我們發送和接受的廣播都是系統的全局廣播,發出的廣播可以被其他任何應用程序接收到。這樣容易引起安全問題,為了解決安全性問題,Android 支持發送本地廣播,其有以下特點:
- 廣播不會離開我們的程序,無需擔心機密數據泄露;
- 其他程序的廣播無法發送到我們程序內部,無需擔心有安全漏洞的隱患;
- 比發送全局廣播更高效。
- 本地廣播的接收只能使用動態注冊,因為靜態注冊就是為了讓程序在未啟動的時候也能接收到廣播,而發送本地廣播的時候應用程序肯定啟動了。
本地廣播并不復雜,主要就是使用了一個 LocalBroadcastManager 來對廣播進行管理,并且提供了發送廣播和注冊廣播接收器的方法:
public class BroadcastActivity extends AppCompatActivity {private static final String TAG = "BroadcastActivity";private IntentFilter intentFilter; // 意圖過濾器private LocalReceiver localReceiver; // 自定義接收器類private LocalBroadcastManager localBroadcastManager; // support包提供的本地廣播工具@SuppressLint("WrongConstant")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.broad_layout);localBroadcastManager = LocalBroadcastManager.getInstance(this); // 獲取實例Button button = findViewById(R.id.button_broadcast1);button.setOnClickListener((View v)->{Log.e(TAG, "onCreate: start");// 將要發送的廣播植入IntentIntent intent = new Intent("com.example.activitytest.Activity.LOCAL_BROADCAST");if(Build.VERSION.SDK_INT >= 28){// 突破隱式廣播限制intent.addFlags(0x01000000);}localBroadcastManager.sendBroadcast(intent); // 發送本地廣播});// 動態注冊的步驟intentFilter = new IntentFilter();// 添加自定義廣播intentFilter.addAction("com.example.activitytest.Activity.LOCAL_BROADCAST");// 實例化接收器localReceiver = new LocalReceiver();// 注冊接收器localBroadcastManager.registerReceiver(localReceiver, intentFilter);}// 動態注冊一定要在結束時取消注冊@Overrideprotected void onDestroy() {super.onDestroy();localBroadcastManager.unregisterReceiver(localReceiver);}class LocalReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "本地廣播", Toast.LENGTH_LONG).show();}}
}