Android 中的BroadCastReceiver
作者:阿福
BroadCastReceiver 簡介 (末尾有源碼)
BroadCastReceiver 源碼位於: framework/base/core/java/android.content.BroadcastReceiver.java
廣播接收者( BroadcastReceiver )用於接收廣播 Intent ,廣播 Intent 的發送是通過調用Context.sendBroadcast() 、 Context.sendOrderedBroadcast() 來實現的。通常一個廣播 Intent 可以被訂閱了此Intent 的多個廣播接收者所接收。
廣播是一種廣泛運用的在應用程序之間傳輸信息的機制 。而 BroadcastReceiver 是對發送出來的廣播進行過濾接收並響應的一類組件;
來自普通應用程序,如一個應用程序通知其他應用程序某些數據已經下載完畢。
BroadcastReceiver 自身並不實現圖形用戶界面,但是當它收到某個通知後, BroadcastReceiver 可以啓動Activity 作爲響應,或者通過 NotificationMananger 提醒用戶,或者啓動 Service 等等。
BroadCastReceiver 的機制
1.機制
在 Android 裏面有各種各樣的廣播,比如電池的使用狀態,電話的接收和短信的接收都會產生一個廣播,應用程序開發者也可以監聽這些廣播並做出程序邏輯的處理。如圖:
2.實現
用接收短信舉例:
第一種方式 :
實現
publicclassMyBroadcastReceiver extendsBroadcastReceiver {
// action 名稱
String SMS_RECEIVED= "android.provider.Telephony.SMS_RECEIVED";
publicvoidonReceive(Context context, Intent intent) {
if(intent.getAction().equals(SMS_RECEIVED)) {
// 相關處理 : 地域變換、電量不足、來電來信;
}
}
}
系統註冊:在 AndroidManifest.xml 中註冊
<receiverandroid:name = ".MyBroadcastReceiver">
<intent-filterandroid:priority = "1000">
<actionandroid:name = "android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>當然了需要權限 :
<uses-permissionandroid:name = "android.permission.RECEIVE_SMS"/>
<uses-permissionandroid:name = "android.permission.SEND_SMS"/>
第二種方式:
// 廣播接收者 - 廣播的接收
privateBroadcastReceiver myBroadcastReceiver= newBroadcastReceiver() {
@Override
publicvoidonReceive(Context context, Intent intent) {
// 相關處理,如收短信,監聽電量變化信息
}
};
代碼中註冊:
IntentFilter intentFilter = newIntentFilter("android.provider.Telephony.SMS_RECEIVED ");
registerReceiver(mBatteryInfoReceiver, intentFilter);
3.生命週期
描述了 Android 中廣播的生命週期,其次它並不像 Activity 一樣複雜,運行原理很簡單如下圖:
生命週期只有十秒左右,如果在 onReceive() 內做超過十秒內的事情,就會報錯 。
每次廣播到來時 , 會重新創建 BroadcastReceiver 對象 , 並且調用 onReceive() 方法 , 執行完以後 , 該對象即被銷燬 . 當 onReceive() 方法在 10 秒內沒有執行完畢, Android 會認爲該程序無響應 . 所以在
BroadcastReceiver 裏不能做一些比較耗時的操作 , 否側會彈出 ANR(Application No
Response) 的對話框 . 。(如圖):
怎麼用好 BroadcastReceiver ?
如果需要完成一項比較耗時的工作 , 應該通過發送 Intent 給 Service, 由 Service 來完成 . 這裏不能使用子線程來解決 , 因爲 BroadcastReceiver 的生命週期很短 , 子線程可能還沒有結束
BroadcastReceiver 就先結束了 .BroadcastReceiver 一旦結束 , 此時 BroadcastReceiver 的
所在進程很容易在系統需要內存時被優先殺死 , 因爲它屬於空進程 ( 沒有任何活動組件的進程 ). 如果它的宿主進程被殺死 , 那麼正在工作的子線程也會被殺死 . 所以採用子線程來解決是不可靠的 .
廣播類型及廣播的收發
廣播類型
普通廣播 (Normal broadcasts)
發送一個廣播,所以監聽該廣播的廣播接收者都可以監聽到改廣播。
異步廣播 , 當處理完之後的Intent ,依然存在,這時候registerReceiver(BroadcastReceiver, IntentFilter) 還能收到他的值,直到你把它去掉 , 不能將處理結果傳給下一個接收者 , 無法終止廣播 .
有序廣播 (Ordered broadcasts)
按照接收者的優先級順序接收廣播 , 優先級別在 intent-filter 中的 priority 中聲明 ,-1000 到
1000 之間 , 值越大 , 優先級越高 . 可以終止廣播意圖的繼續傳播 . 接收者可以篡改內容 .
廣播的收發
該組件接收被廣播的 intent,Context 可以通過 sendBroadcast() 和 sendOrderedBroadcast()
方法實現廣播的發送 .
首先在需要發送信息的地方 ,把要發送的信息和用於過濾的信息 ( 如 Action 、 Category) 裝入一個 Intent對象,然後通過調用 Context.sendBroadcast() 、 sendOrderBroadcast() 或 sendStickyBroadcast() 方法,把 Intent對象以廣播方式發送出去。
使用 sendBroadcast() 或 sendStickyBroadcast() 方法發出去的 Intent ,所有滿足條件的 BroadcastReceiver 都會隨機地執行其 onReceive() 方法
普通廣播的發送和接收:
sendBroadcast(intent);
Intent intent = newIntent("cn.lenovo.yangguangf ");
sendBroadcast(intent);
priority :這個是 AndroidManifest.xml 中 intent-filter 的參數。
<receiverandroid:name = ".MyBroadcastReceiver">
<intent-filterandroid:priority = "1000">
<actionandroid:name = "cn.lenovo.yangguangfu"/>
</intent-filter>
</receiver>
sendOrderedBroadcast(intent, receiverPermission);
1 ,他決定該廣播的級別,級別數值是在 -1000 到 1000 之間 , 值越大 , 優先級越高;
2 ,同級別接收是先後是隨機的;級別低的收到廣播;
3 ,在 android 系統中只要監聽該廣播的接收者,都能夠收到 sendBroadcast(intent) 發出的廣播 ;
3 ,不能截斷廣播的繼續傳播,
4 ,實驗現象,在這個方法發來的廣播中,代碼註冊方式中,收到的廣播的先後和註明優先級最高的他們的先後是隨機。如果都沒有優先級,代碼註冊收到爲最先。
有序廣播的發送和接收:
sendOrderedBroadcast(intent, receiverPermission);
sendOrderedBroadcast(intent, receiverPermission, resultReceiver,
scheduler, initialCode, initialData, initialExtras)
意圖,廣播,所有匹配的這一意圖將接收機接收廣播。
receiverPermission 這是權限,一個接收器必須持以接收您的廣播。如果爲 null ,不經許可的要求。
resultReceiver 您自己 BroadcastReceiver 來當作最後的廣播接收器。
調度自定義處理程序,用以安排 resultReceiver 回調 ; 如果爲 null 將語境中的主線程舉行。
initialCode 一種結果代碼的初始值。通常爲 Activity.RESULT_OK 。這個值是 -1 ;爲其他 int 型 也可以,如 0,1,2;
initialData 一種結果數據的初始值。通常情況下爲空 , 是 String 類型 ;
initialExtras 一種結果額外的初始值。通常情況下爲空 , 是 Bundle;
intent The Intent to broadcast; all receivers matching this Intent will receive the broadcast.
receiverPermission String naming a permissions that a receiver must hold in order to receive your broadcast. If null, no permission is required.
resultReceiver Your own BroadcastReceiver to treat as the final receiver of the broadcast.
scheduler A custom Handler with which to schedule the resultReceiver callback; if null it will be scheduled in the Context's main thread.
initialCode An initial value for the result code. Often Activity.RESULT_OK.
initialData An initial value for the result data. Often null.
initialExtras An initial value for the result extras. Often null.
1, 該廣播的級別有級別之分,級別數值是在 -1000 到 1000 之間 , 值越大 , 優先級越高;
2, 同級別接收是先後是隨機的,再到級別低的收到廣播;
3, 同級別接收是先後是隨機的,如果先接收到的把廣播截斷了,同級別的例外的接收者是無法收到該廣播的。(abortBroadcast() )
4 ,能截斷廣播的繼續傳播,高級別的廣播收到該廣播後,可以決定把該鐘廣播是否截斷掉。
5 ,實驗現象,在這個方法發來的廣播中,代碼註冊方式中,收到廣播先後次序爲:註明優先級的、代碼註冊的、沒有優先級的;如果都沒有優先級,代碼註冊收到爲最先。
異步廣播的發送和接收:
sendStickyBroadcast(intent);
當處理完之後的Intent ,依然存在,直到你把它去掉。
發這個廣播需要權限<uses-permission android:name="android.permission.BROADCAST_STICKY" />
去掉是用這個方法removeStickyBroadcast(intent); 但別忘了在執行這個方法的應用裏面 AndroidManifest.xml同樣要加上面的權限;
sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,
initialCode, initialData, initialExtras)
這個方法具有有序廣播的特性也有異步廣播的特性;
發送這個廣播要: <uses-permission android:name="android.permission.BROADCAST_STICKY" /> 這個權限。才能使用這個方法。如果您並不擁有該權限,將拋出 SecurityException 的。
實驗現象( sendStickyOrderedBroadcast ()中),在這個方法發來的廣播中,代碼註冊方式中,收到廣播先後次序爲:註明優先級的、代碼註冊的、沒有優先級的;如果都沒有優先級,代碼註冊收到爲最先。
廣播註冊與註銷
代碼中註冊廣播:
註冊廣播方法一: registerReceiver(BroadcastReceiver receiver, IntentFilter filter) ,第一個參數是我們要處理廣播的 BroadcastReceiver (廣播接收者,可以是系統的,也可以是自定義的);第二個參數是意圖過濾器。
註冊廣播方法二: registerReceiver(receiver, filter, broadcastPermission, scheduler) ,第一個參數是BroadcastReceiver (廣播接收者,可以是系統的,也可以是自定義的);第二個參數是意圖過濾器;第三個參數是廣播權限;第四個參數是 Hander ;
注意:權限重複現象,如果功能清單文件裏註冊了權限,在該方法再註冊,則 receiver 無法收到廣播,如果 功能清單文件裏沒有註冊了權限,該方法註冊也無法收到。當該方法沒有註冊權限,功能清單裏註冊的時候, receiver 能收到廣播。
總結:在 Activity 中代碼註冊廣播建議在: onResume() 中註冊;
思維拓展: 1 ,如果在代碼調用 registerReceiver(BroadcastReceiver receiver, IntentFilter filter) 十次( receiver, filter 的參數是同一參數),那麼是否當該廣播發送來的時候會收到十次呢?
2 ,註銷是否也要註銷十次才能把廣播全部註銷呢?
系統中註冊廣播:(在 AndroidManifest.xml 中 )
<receiverandroid:name = ".MyBroadcastReceiver">
<intent-filterandroid:priority = "900">
<actionandroid:name = "cn.lenovo.yangguangfu"/>
</intent-filter>
</receiver>
有時候還要根據發送廣播是否指定權限,來決定是否要權限;
廣播註銷
// 代碼中註銷廣播
/unregisterReceiver(mBatteryInfoReceiver);
在 Activity 中代碼註銷廣播建議在: onPuase() 中註銷;
不要這這裏面註銷 Activity.onSaveInstanceState(), 因爲這個方法是保存 Intent 狀態的。
BroadCastReceiver 的 API
abortBroadcast():
這個方法可以截獲由 sendOrderedBroadcast () 發送來的 廣播,讓其它廣播接收者無法收到這個廣播。
clearAbortBroadcast ()
這個方法是針對上面的 abortBroadcast() 方法的,用於取消截獲廣播。這樣它的下一級廣播接收者就能夠收到該廣播了。
getAbortBroadcast ()
這個方法作用是:判斷是否調用了 abortBroadcast (),如果先調用 abortBroadcast (),接着再調用getAbortBroadcast (),將返回 true; 如果在調用 abortBroadcast() 、 clearAbortBroadcast ()
getAbortBroadcast (),將返回 false;
public final boolean getDebugUnregister ()
Since: API Level 1
Return the last value given to setDebugUnregister(boolean)
.
getResultCode ()
如果用下面四個方法發送得廣播,返回碼爲: -1 ;
// sendBroadcast(intent);
// sendBroadcast(intent, receiverPermission);
// sendOrderedBroadcast(intent, receiverPermission);
// sendStickyBroadcast(intent);
如果用下面兩個方法發送得廣播,返回碼爲:根據你設置 initialCode 的數字是多少就是多少;
// sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,
// initialCode, initialData, initialExtras)
// sendOrderedBroadcast(intent, receiverPermission, resultReceiver,
// scheduler, initialCode, initialData, initialExtras)
getResultData ()
得到發送廣播時設置的 initialData 的數據;
getResultExtras(boolean makeMap)
If true then a new empty Map will be made for you if the current Map is null; if false you should be prepared to receive a null Map.
得到由
sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,
// initialCode, initialData, initialExtras) ;
// sendOrderedBroadcast(intent, receiverPermission, resultReceiver,
// scheduler, initialCode, initialData, initialExtras)
中 initialExtras 傳入的參數。
實驗:我用上面兩個方法發了 initialExtras (這個一個 Bundle )傳入的參數時,只要不爲空,那麼 makeMap是否爲 true 和 false 都能夠得到數據。
isInitialStickyBroadcast ()
Returns true if the receiver is currently processing the initial value of a sticky broadcast -- that is, the value that was last broadcast and is currently held in the sticky cache, so this is not directly the result of a broadcast right now.
如果廣播接收者是目前處理的一個宿主的廣播的初始值,將返回 true , - 也就是說,這個值是最後的廣播出的值,目前正在舉行的宿主緩存,所以這並不是直接導致了現在的廣播。
實驗:在第三個應用中調用這個方法,無論你用哪種方式發送廣播,這個方法得到的總是 false ;在發送廣播 的resultReceiver 廣播接收者裏面調用,得到的也是 false ;
isOrderedBroadcast ()
sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,
initialCode, initialData, initialExtras)
上面這個方法發送時,得到的是 true;
判斷是否是有序廣播;
onReceive (Context context, Intent intent)
public IBinder peekService (Context myContext, Intent service)
Provide a binder to an already-running service. This method is synchronous and will not start the target service if it is not present, so it is safe to call from onReceive.
Parameters:
myContext The Context that had been passed to onReceive(Context, Intent)
service The Intent indicating the service you wish to use. See Context.startService(Intent) for more information.
setDebugUnregister (boolean debug)
Control inclusion of debugging help for mismatched calls to {@ Context#registerReceiver(BroadcastReceiver, IntentFilter) Context.registerReceiver()}. If called with true, before given to registerReceiver(), then the callstack of the following Context.unregisterReceiver()
call is retained, to be printed if a later incorrect unregister call is made. Note that doing this requires retaining information about the BroadcastReceiver for
頂3
踩