Android入門教程 | 廣播機制 Broadcast
Android應用可以透過廣播從系統或其他App接收或傳送訊息。類似於訂閱-釋出設計模式。當某些事件發生時,可以發出廣播。 系統在某些狀態改變時會發出廣播,例如開機、充電。App也可傳送自定義廣播。廣播可用於應用間的通訊,是IPC的一種方式。
廣播的種類
廣播的種類也可以看成是廣播的屬性。
-
標準廣播(Normal Broadcasts)
完全非同步的廣播。廣播發出後,所有的廣播接收器幾乎同時接收到這條廣播。 不同的App可以註冊並接到標準廣播。例如系統廣播。 -
有序廣播(Ordered Broadcasts)
同步廣播。同一時刻只有一個廣播接收器能接收到這條廣播。這個接收器處理完後,廣播才會繼續傳遞。 有序廣播是全域性的廣播。 -
本地廣播(Local Broaddcasts)
只在本App傳送和接收的廣播。註冊為本地廣播的接收器無法收到標準廣播。 -
帶許可權的廣播
傳送廣播時可以帶上相關許可權,申請了許可權的 App 或廣播接收器才能收到相應的帶許可權的廣播。 如果在 manifest 中申請了相應許可權,接收器可以不用再申請一次許可權即可接到相應廣播。
接收廣播
建立廣播接收器,呼叫
onReceive()
方法,需要一個繼承 BroadcastReceiver 的類。
註冊廣播
程式碼中註冊稱為動態註冊。在
AndroidManifest.xml
中註冊稱為靜態註冊。動態註冊的剛波接收器一定要取消註冊。在
onDestroy()
方法中呼叫
unregisterReceiver()
方法來取消註冊。
不要在
onReceive()
方法中新增過多的邏輯操作或耗時的操作。因為在廣播接收器中不允許開啟執行緒,當
onReceive()
方法執行較長時間而沒結束時,程式會報錯。因此廣播接收器一般用來開啟其他元件,比如建立一條狀態列通知或啟動一個服務。
新建一個 MyExampleReceiver繼承自 BroadcastReceiver。
public class MyExampleReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"Got it",Toast.LENGTH_SHORT).show(); //abortBroadcast(); } }
abortBroadcast()
可以截斷有序廣播
在
AndroidManifest.xml
中註冊廣播接收器;
android:name
裡填接收器的名字。 可以設定廣播接收器優先順序:
<intent-filter android:priority="100"><receiver android:name=".MyExampleReceiver"> <intent-filter> <action android:name="com.rust.broadcasttest.MY_BROADCAST"/> </intent-filter></receiver>
讓接收器接收到一條
"com.rust.broadcasttest.MY_BROADCAST"
廣播。
傳送自定義廣播(標準廣播)時,要傳送這個值。例如:
Intent intent = new Intent("com.rust.broadcasttest.MY_BROADCAST"); sendBroadcast(intent);
傳送有序廣播,應當呼叫
sendOrderedBroadcast()
Intent intent = new Intent("com.rust.broadcasttest.MY_BROADCAST"); sendOrderedBroadcast(intent,null);
傳送廣播
App有3種傳送廣播的方式。傳送廣播需要使用 Intent類。
sendOrderedBroadcast(Intent, String)
傳送有序廣播。每次只有1個廣播接收器能接到廣播。 接收器接到有序廣播後,可以完全地截斷廣播,或者傳遞一些資訊給下一個接收器。 有序廣播的順序可受
android:priority
標籤影響。同等級的接收器收到廣播的順序是隨機的。
sendBroadcast(Intent)
以一個未定義的順序向所有接收器傳送廣播。也稱作普通廣播。 這種方式更高效,但是接收器不能給下一個接收器傳遞訊息。這類廣播也無法截斷。
LocalBroadcastManager.sendBroadcast
廣播只能在應用程式內部進行傳遞,並且廣播接收器也只能接收到來自本應用程式發出的廣播。 這個方法比全域性廣播更高效(不需要Interprocess communication,IPC),而且不需要擔心其它App會收到你的廣播以及其他安全問題。
廣播與許可權
傳送帶著許可權的廣播
當你呼叫
sendBroadcast(Intent, String)
或
sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
時,你可以指定一個許可權。
接收器在manifest中申請了相應許可權時才能收到這個廣播。
例如傳送一個帶著許可權的廣播
sendBroadcast(new Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS);
接收廣播的app必須註冊相應的許可權
<uses-permission android:name="android.permission.SEND_SMS"/>
當然也可以使用自定義。在 manifest 中使用 permission 標籤
<permission android:name="custom_permission" />
新增後編譯一下。即可呼叫
Manifest.permission.custom_permission
接收帶許可權的廣播
若註冊廣播接收器時申明瞭許可權,那麼只會接收到帶著相應許可權的廣播。
在配置檔案中宣告許可權,程式才能訪問一些關鍵資訊。 例如允許查詢系統網路狀態。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><!-- 機器開機廣播 --><uses-permission android:name="android.permission.BOOT_COMPLETED">
如果沒有申請許可權,程式可能會意外關閉。
使用示例
傳送和接收廣播。分為傳送和接收方2個App。
使用帶許可權的廣播。系統許可權與自定義許可權。 使用許可權需要在 AndroidManifest.xml中宣告。如果是自定義許可權,需要先新增自定義許可權。
<!-- 自定義的許可權 給廣播用 --><permission android:name="com.rust.permission_rust_1" /><uses-permission android:name="com.rust.permission_rust_1" />
傳送廣播時帶上許可權宣告。接收方(不論是否己方App)需要在
AndroidManifest.xml
中申請許可權。 註冊接收器時也需要宣告許可權。
傳送不帶許可權的有序廣播
Intent intent = new Intent(MSG_PHONE); sendOrderedBroadcast(intent, null);Log.d(TAG, "[RustFisher-App1] 傳送不帶許可權的有序廣播, " + intent.getAction());
傳送方App1程式碼
private static final String TAG = "rustApp"; public static final String MSG_PHONE = "msg_phone"; public static final String PERMISSION_RUST_1 = "com.rust.permission_rust_1"; // onCreate註冊廣播接收器 registerReceiver(mStandardReceiver1, makeIF()); registerReceiver(mStandardReceiver2, makeIF()); registerReceiver(mStandardReceiver3, makeIF()); registerReceiver(mStandardReceiverWithPermission, makeIF(), Manifest.permission.permission_rust_1, null); // 帶上許可權 LocalBroadcastManager.getInstance(getApplicationContext()) .registerReceiver(mLocalReceiver1, makeIF()); LocalBroadcastManager.getInstance(getApplicationContext()) .registerReceiver(mLocalReceiver2, makeIF()); LocalBroadcastManager.getInstance(getApplicationContext()) .registerReceiver(mLocalReceiver3, makeIF()); // 解除接收器 unregisterReceiver(mStandardReceiver1); unregisterReceiver(mStandardReceiver2); unregisterReceiver(mStandardReceiver3); unregisterReceiver(mStandardReceiverWithPermission); LocalBroadcastManager.getInstance(getApplicationContext()) .unregisterReceiver(mLocalReceiver1); LocalBroadcastManager.getInstance(getApplicationContext()) .unregisterReceiver(mLocalReceiver2); LocalBroadcastManager.getInstance(getApplicationContext()) .unregisterReceiver(mLocalReceiver3); // 傳送標準廣播 private void sendStandardBroadcast() { Intent intent = new Intent(MSG_PHONE); sendBroadcast(intent); Log.d(TAG, "[RustFisher-App1] Dispatcher 傳送標準廣播"); } // 傳送帶許可權的標準廣播 private void sendStandardBroadcastWithPermission() { Intent intent = new Intent(MSG_PHONE); sendBroadcast(intent, PERMISSION_RUST_1); Log.d(TAG, "[RustFisher-App1] Dispatcher 傳送帶許可權的標準廣播"); } // 傳送本地廣播 private void sendAppLocalBroadcast() { Intent intent = new Intent(MSG_PHONE); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); Log.d(TAG, "[RustFisher-App1] Dispatcher 傳送本地廣播"); } private IntentFilter makeIF() { IntentFilter intentFilter = new IntentFilter(MSG_PHONE); intentFilter.addAction(Intent.ACTION_TIME_TICK); intentFilter.addAction(Intent.ACTION_TIME_CHANGED); return intentFilter; } // 標準接收器 用context來註冊 private BroadcastReceiver mStandardReceiver1 = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 標準接收器1 收到: " + intent.getAction()); } }; // 標準接收器 用context來註冊 private BroadcastReceiver mStandardReceiver2 = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 標準接收器2 收到: " + intent.getAction()); if (intent.getAction().endsWith(MSG_PHONE)) { abortBroadcast(); // 截斷有序廣播 Log.d(TAG, "[RustFisher-App1] 標準接收器2截斷有序廣播 " + intent.getAction()); } } }; // 標準接收器 用context來註冊 private BroadcastReceiver mStandardReceiver3 = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 標準接收器3 收到: " + intent.getAction()); } }; // 註冊的時候給它帶許可權 標準接收器 private BroadcastReceiver mStandardReceiverWithPermission = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 帶許可權的標準接收器收到: " + intent.getAction()); } }; /** * 用LocalBroadcastManager來註冊成為本地接收器 * 收不到標準廣播 - 不論是本app發出的還是別的地方發出來的 */ private BroadcastReceiver mLocalReceiver1 = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 本地接收器1 收到: " + intent.getAction()); } }; private BroadcastReceiver mLocalReceiver2 = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 本地接收器2 收到: " + intent.getAction()); } }; private BroadcastReceiver mLocalReceiver3 = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[RustFisher-App1] 本地接收器3 收到: " + intent.getAction()); } };
接收方App2程式碼
<!-- 自定義的許可權 給廣播用 --><permission android:name="com.rust.permission_rust_1" /><uses-permission android:name="com.rust.permission_rust_1" />
public static final String MSG_PHONE = "msg_phone"; // onCreate裡註冊接收器 registerReceiver(mDefaultReceiver, makeIF()); LocalBroadcastManager.getInstance(getApplicationContext()) .registerReceiver(mLocalReceiver, makeIF()); unregisterReceiver(mDefaultReceiver); LocalBroadcastManager.getInstance(getApplicationContext()) .unregisterReceiver(mLocalReceiver); private BroadcastReceiver mDefaultReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[App2] standard receive: " + intent.getAction()); } }; private BroadcastReceiver mLocalReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "[App2] local receive: " + intent.getAction()); } }; private IntentFilter makeIF() { IntentFilter intentFilter = new IntentFilter(MSG_PHONE); intentFilter.addAction(Intent.ACTION_TIME_TICK); intentFilter.addAction(Intent.ACTION_TIME_CHANGED); return intentFilter; }
使用
LocalBroadcastManager
發出的本地廣播,另一個App是接收不到的。 要收到本地廣播,同樣需要
LocalBroadcastManager
來註冊接收器。
可以把本地廣播看成是一個區域性的,App內的廣播體系。
實驗中我們注意到,
Intent.ACTION_TIME_TICK
廣播是可以截斷的。
監聽螢幕亮滅
使用廣播監聽裝置螢幕亮滅狀態。這個是系統發出來的廣播。
使用的action是
- Intent.ACTION_SCREEN_ON 亮屏
-
Intent.ACTION_SCREEN_OFF 滅屏
private void registerScreenListener() { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); registerReceiver(mScreenReceiver, filter); } private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (Intent.ACTION_SCREEN_ON.equals(action)) { // 螢幕亮 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 螢幕滅 } } };
Broadcast 相關面試題
1. 廣播傳輸的資料是否有限制,是多少,為什麼要限制?
- 廣播是透過Intent攜帶需要傳遞的資料的
- Intent是透過Binder機制實現的
- Binder對資料大小有限制,不同room不一樣,一般為1M
2. 廣播的分類?
- 標準廣播:透過context. sendBroadcast或者context. sendBroadcastAsUser傳送給當前系統中所有註冊的接受者,也就是隻要註冊了就會接收到。應用在需要通知各個廣播接收者的情況下使用,如開機啟動。
- 有序廣播:接收者按照優先順序處理廣播,並且前面處理廣播的接受者可以中止廣播的傳遞,一般透過context. sendOrderedBroadcast或者context.sendOrderedBroadcastAsUser,在需要有特定攔截的場景下使用,如黑名單簡訊、電話攔截。
- 粘性廣播:可以傳送給以後註冊的接受者,意思是系統會將前面的粘性廣播儲存在AMS中,一旦註冊了與以儲存的粘性廣播符合的廣播,在註冊結束後會立即收到廣播,一般透過context. sendStickyBroadcast或context.sendStickyOrderedBroadcast來傳送,從字面上看,可以看出來粘性廣播也分為普通粘性廣播和有序粘性廣播。
- 本地廣播:發出的廣播只能在應用程式內部進行傳遞,廣播接收器也只能接受來自本應用程式的廣播。
- 全域性廣播:系統和廣播,發出的廣播可以被其他任何應用程式接收到,並且也可以接受到其他任何應用程式的廣播。
3. 廣播的使用場景,使用方式
廣播是一種廣泛運用的在應用程式之間傳輸資訊的機制,主要用來監聽系統或者應用發出的廣播資訊,然後根據廣播資訊作為相應的邏輯處理,也可以用來傳輸少量、頻率低的資料。
在實現開機啟動服務和網路狀態改變、電量變化、簡訊和來電時透過接收系統的廣播讓應用程式作出相應的處理。
使用:
//在AndroidManifest中靜態註冊<receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.example.hp.broadcasttest.MY_BROADCAST"/> </intent-filter> </receiver>//動態註冊,在程式碼中註冊@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mIntentFilter = new IntentFilter(); //新增廣播想要監聽的型別,監聽網路狀態是否發生變化 mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); mNetworkChangeReceiver = new NetworkChangeReceiver(); //註冊廣播 registerReceiver(mNetworkChangeReceiver, mIntentFilter); } @Override protected void onDestroy() { super.onDestroy(); //取消註冊廣播接收器 unregisterReceiver(mNetworkChangeReceiver); }//傳送廣播,同樣透過IntentIntent intent = new Intent("com.example.hp.broadcasttest.MY_BROADCAST");//傳送標準廣播sendBroadcast(intent);//接收廣播public class MyBroadcastReceiver extends BroadcastReceiver { public MyBroadcastReceiver() { } @Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context, "received", Toast.LENGTH_SHORT).show(); //將這條廣播截斷// abortBroadcast(); } }
4. BroadcastReceiver,LocalBroadcastReceiver 區別
廣播接收者:
(1)用於應用間的傳遞訊息 (2)由於跨應用,存在安全問題
本地廣播接收者:
(1)廣播資料在本應用範圍內傳播。 (2)不用擔心別的應用偽造廣播。 (3)比傳送全域性廣播更高效、安全。 (4)無法使用靜態註冊
5. 在 manifest 和程式碼中如何註冊和使用 BroadcastReceiver
(1)在AndroidManifest中靜態註冊,然後直接使用。
(2)程式碼中,透過registerReceiver來註冊。
(3)註冊傳送後,在BroadcastReceiver(自定義一個接收器繼承自BroadcastReceiver)的onReceive中接收廣播並處理廣播。
6. 廣播引起 anr 的時間限制
前臺廣播:BROADCAST_FG_TIMEOUT = 10s
後臺廣播:BROADCAST_BG_TIMEOUT = 60s
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70008155/viewspace-2827062/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android之Broadcast(廣播)AndroidAST
- android: 廣播機制Android
- Android-Broadcast Receiver(廣播接收器)AndroidAST
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃 .AndroidAST
- Python NumPy 廣播(Broadcast)PythonAST
- Android開機廣播和關機廣播Android
- Spark2 broadcast廣播變數SparkAST變數
- Laravel Broadcast——廣播系統原始碼剖析LaravelAST原始碼
- Android開機和關機廣播監聽Android
- Laravel 廣播入門,彈幕的實現Laravel
- 【3分鐘速覽】前端廣播式通訊:Broadcast Channel前端AST
- 用張量廣播機制實現神經網路反向傳播神經網路反向傳播
- Android BroadcastReceiver(廣播)AndroidAST
- Android - BroadcastReceiver 廣播AndroidAST
- Android廣播動作Android
- Android複習–廣播Android
- android: 使用本地廣播Android
- pytorch多維張量相乘和廣播機制示例PyTorch
- Android入門教程 | RecyclerView使用入門AndroidView
- Android中的廣播使用Android
- Android系統廣播(轉)Android
- android廣播集合,intent,actionAndroidIntent
- android: 接收系統廣播Android
- Android之粘性廣播理解Android
- Java入門-核心機制Java
- 廣播入坑到出坑
- android藍芽BLE(三) —— 廣播Android藍芽
- 說說Android的廣播(3)Android
- android: 傳送自定義廣播Android
- Android入門教程 | Kotlin協程入門AndroidKotlin
- Realm for Android快速入門教程Android
- Android SQLite快速入門教程AndroidSQLite
- Android之監測手機網路狀態的廣播Android
- JVM垃圾回收機制入門JVM
- 【Android開發入門教程】三.Activity入門指南!Android
- Android廣播之靜態註冊Android
- Android 廣播許可權保護Android
- Android利用廣播攔截簡訊Android