四大元件之廣播接收者(BroadCastReceiver)
為什麼需要廣播接收者?
生活中有什麼樣的人需要聽廣播呢?比方說老人聽聽廣播解解悶,計程車司機聽聽廣播瞭解一下當前的路況資訊,寂寞的大學生聽一些音樂和情感夜話等等,他們都是有接收廣播的需求的,在android開發中很多app也需要接收這樣的廣播來獲取一些資訊,所以在app內就必須定義廣播接收者(收音機)。
想聽廣播必須有兩樣東西,第一是電臺,第二是收音機。
Android系統內部已經定義好了許多電臺,對應一些廣播事件,比如外撥電話,簡訊到來,sd卡狀態,電池電量等等資訊…
此時我們的APP便可以使用broadcastReceiver去接收系統已經定義好的這些事件。
最需要了解的是:定義廣播接收者的目的是為了方便開發者進行開發的。
下面我們從最簡單的廣播接收者講起,接收系統定義好的一條廣播事件。
廣播接收者案例——ip撥號器
此案例當手機外撥電話時,app接收到廣播並進行一定的處理。
1.新建一個android工程,定義一個類繼承BroadCastReceiver,實現其onReceive()方法。當接收到廣播時,執行onReceive()方法。此處相當於買了一個收音機。
public class OutGoingCallReceiver extends BroadcastReceiver { //當進行外撥電話的時候 呼叫 @Override public void onReceive(Context context, Intent intent) { } } }
在清單檔案中註冊廣播接收者(相當於給收音機安上電池),在意圖過濾器中定義接收的廣播型別為外撥電話,相當於給收音機調頻。
<!-- 配置廣播接收者 --> <receiver android:name="com.example.ipdial.OutGoingCallReceiver"> <intent-filter > <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> </intent-filter> </receiver>
增加相應的許可權(不是每一個廣播接收者都需要增加)
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
這樣一個最簡單的靜態方法註冊廣播接收者就定義好了。當外撥電話時,就會執行onReceive()方法。靜態註冊(通過清單檔案註冊)的廣播接收者在Activity退出後仍然生效。
但是我們需要對廣播接收到的資訊做一些處理,此處在onReceive()方法就不能進行空實現了,需要對接收到的資訊做處理。
public class OutGoingCallReceiver extends BroadcastReceiver {
//當進行外撥電話的時候 呼叫
@Override
public void onReceive(Context context, Intent intent) {
//[1]獲取當前撥打的電話號碼
String currentNumber=getResultData();
System.out.println("currentNumber:"+currentNumber);
//[2]在當前的號碼前面加上17951
//[2.1]判斷當前撥打的號碼是否是長途
if(currentNumber.startsWith("0")){
//[2.2]修改撥打電話的號碼
setResultData(“17951”+currentNumber);
System.out.println("聯合內容:"+“17951”+currentNumber);
}
}
}
這樣外撥電話時,就會在電話號碼前自動加上17951.
其中getResultData()是獲得廣播的內容。
setResultData();是修改廣播的內容。
案例二——在廣播中開啟Activity
注意,在廣播接收者中是不能直接開啟Activity的。如果需要開啟,需要新增一個任務棧的標記。
此案例實現在重啟時開啟Activity,並且禁用返回鍵,使得當前裝置只能使用當前開啟的app。
MainActivity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//當按返回鍵的時候呼叫此方法
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
//此方法呼叫父類的finish()方法,如果註釋掉,則返回鍵會失效。
//super.onBackPressed();
}
}
註冊廣播接收者,使得在收到重啟廣播時開啟指定Activity
public class BootReciver extends BroadcastReceiver{
//當手機重新啟動的時候呼叫
@Override
public void onReceive(Context context, Intent intent) {
//在這個方法裡開啟activity
Intent intent2=new Intent(context,MainActivity.class);
//&&&注意 不能在廣播接收者裡直接開啟activity 需要新增一個標記 新增一個任務棧的標記
intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//由上下文開啟activity
context.startActivity(intent2);
}
}
清單檔案中註冊,“調頻”至手機重啟時接收廣播。
<receiver android:name="com.example.bootreciver.BootReciver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
增加許可權
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
這樣,在重啟裝置時,將直接開啟該應用介面,並禁用返回鍵(可以同時禁用home鍵)。
接下來我們討論廣播的型別——有序廣播和無序廣播
1有序廣播
類似中央傳送紅標頭檔案,按照一定的優先順序進行接收
特徵:
①有序廣播可以被終止
②有序廣播的資料可以被修改
2無序廣播
比如新聞聯播 每天晚上7點準時開播
特點:
①無序廣播不可以終止。
②資料不可以被修改。
之前討論的兩個案例都是系統定義好的廣播,我們接收就可以了。現在我們以傳送無序廣播和有序廣播為例,來自定義傳送廣播,自己來搭建電臺。
傳送無序廣播
MainActivity
//點選按鈕傳送一條無序廣播
public void click(View v){
Intent intent=new Intent();
intent.setAction("com.exmple.custom");//定義廣播接收者在清單檔案中應該指定的action
intent.putExtra("name", "新聞聯播每天晚上七點準時開播");
sendBroadcast(intent);//傳送無序廣播
}
傳送廣播到此為止,不用定義清單檔案之類的東西。主要程式碼就是sendBroadcast(intent);
用intent來指定廣播的名稱及內容。
接收無序廣播
在另一個應用中定義廣播接收者
public class WuxuReceiver extends BroadcastReceiver {
//當接收到我們傳送的自定義無序廣播時執行
@Override
public void onReceive(Context context, Intent intent) {
String content=intent.getStringExtra("name");
Toast.makeText(context, content, 1).show();
}
}
清單檔案宣告廣播接收者
<receiver android:name="com.example.receivewuxubroadcast.WuxuReceiver">
<intent-filter>
<action android:name="com.exmple.custom"/>
</intent-filter>
</receiver>
當傳送廣播時,第二個應用就會列印"新聞聯播每天晚上七點準時開播"的Toast資訊。
傳送有序廣播
MainActivity
//點選按鈕 傳送有序廣播 發大米
public void click(View v){
Intent intent=new Intent();
intent.setAction("com.example.sendrice");
/*intent 意圖
* receiverPermission 接收許可權
* resultReceiver 最終的receiver
* scheduler handler
* initialCode 初始碼
* initialData 初始化資料
* initialExtras 額外的資料
* */
sendOrderedBroadcast(intent, null, new FinalReciver(),
null, 1,"習大大給每個人發了一千斤大米" ,null );
}
接收有序廣播
一級一級接收,定義省,市,鄉,農民四個接收級別。
省
public class ProvienceReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取傳送廣播攜帶的資料
//廣播攜帶資料用getResultData(); intent攜帶用getStringExtra();
String content = getResultData();
Toast.makeText(context,"省:"+content,Toast.LENGTH_SHORT).show()
}
市
public class CityReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取傳送廣播攜帶的資料
//廣播攜帶資料用getResultData(); intent攜帶用getStringExtra();
String content = getResultData();
Toast.makeText(context,"市:"+content,Toast.LENGTH_SHORT).show();
}
}
鄉
public class CountryReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取傳送廣播攜帶的資料
//廣播攜帶資料用getResultData(); intent攜帶用getStringExtra();
String content = getResultData();
Toast.makeText(context,"鄉:"+content,Toast.LENGTH_SHORT).show();
}
}
農民
public class NongminReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取傳送廣播攜帶的資料
//廣播攜帶資料用getResultData(); intent攜帶用getStringExtra();
String content = getResultData();
Toast.makeText(context,"農民:"+content,Toast.LENGTH_SHORT).show();
}
}
清單檔案中宣告優先順序
優先順序一般是1000以下,有相對的大小即可。
<receiver android:name=".ProvienceReciever">
<intent-filter android:priority="1000">
<action android:name="com.example.sendrice">
</action>
</intent-filter>
</receiver>
<receiver android:name=".CityReciever">
<intent-filter android:priority="500">
<action android:name="com.example.sendrice">
</action>
</intent-filter>
</receiver>
<receiver android:name=".CountryReciever">
<intent-filter android:priority="100">
<action android:name="com.example.sendrice">
</action>
</intent-filter>
</receiver><receiver android:name=".NongminReciever">
<intent-filter android:priority="-100">
<action android:name="com.example.sendrice">
</action>
</intent-filter>
</receiver>
這樣傳送完廣播後,廣播就會從省市鄉農民一級一級往下傳。
有序廣播可以被更改:比如廣播發到省裡時是1000斤大米,省裡在往下發的時候剋扣了500斤。
public class ProvienceReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取傳送廣播攜帶的資料
//廣播攜帶資料用getResultData(); intent攜帶用getStringExtra();
String content = getResultData();
Toast.makeText(context,"省:"+content,Toast.LENGTH_SHORT).show();
//修改廣播資料
setResultData("習大大給每人發了500斤大米");
}
}
這樣廣播在往下發的時候就變成了"習大大給每人發了500斤大米"。
如果省裡財政緊張,把大米全部截留了,不再往下發,這就屬於終止廣播,之後的優先順序低的市、鄉、農民廣播接收者都不能在收到廣播。
public class ProvienceReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取傳送廣播攜帶的資料
//廣播攜帶資料用getResultData(); intent攜帶用getStringExtra();
String content = getResultData();
Toast.makeText(context,"省:"+content,Toast.LENGTH_SHORT).show();
//終止廣播
abortBroadcast();
}
}
但是習大大不會任由廣播接收者截留,所以在傳送有序廣播時定義看最終的廣播接收者,無論該條廣播是不是被終止了,最終的廣播接收者都會收到最後的廣播資訊,檢查是否有人剋扣大米,報告給習大大。
在傳送有序廣播的應用中定義最終的廣播接收者:
注意在傳送有序廣播時第三個引數,必須在引數中拿到此最終的廣播接收者例項才能不用註冊。
public class FinalReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//FinalReciever有特權,不用在清單檔案中註冊,並且就算廣播被終止,最後FinalReciever也能收到廣播。
//用來檢查廣播傳遞的情況。
Toast.makeText(context,"報告習大大:"+getResultData(),Toast.LENGTH_SHORT).show();
}
}
此時執行程式,無論是否被終止, FinalReciever都會最後收到該廣播資訊。
最後一個重點:
廣播的靜態註冊和動態註冊。
靜態註冊:所有在清單檔案中宣告的廣播接收者註冊方式。
動態註冊:不在清單檔案中宣告,通過程式碼的方式進行廣播接收者的註冊。
以上講的所有註冊方式都是靜態註冊,就算Activity退出,廣播接收者依然生效。
動態註冊的廣播接收者一旦註冊它的Activity退出,廣播也隨之失效。
如果該廣播頻繁被髮送,則監聽該廣播的廣播接收者應該使用動態註冊的方式。比如解鎖屏。
MainActivity
動態註冊廣播接收者
public class MainActivity extends AppCompatActivity {
private ScreenBroadCastReciever screenBroadCastReciever;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screenBroadCastReciever = new ScreenBroadCastReciever();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.SCREEN_OFF");
intentFilter.addAction("android.intent.action.SCREEN_ON");
//動態註冊廣播接收者
registerReceiver(screenBroadCastReciever,intentFilter);
}
廣播接收者定義
//註冊操作頻繁的廣播接收者(例如解鎖螢幕),在清單檔案中配置無效,在程式碼中動態配置。
public class ScreenBroadCastReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取當前廣播的事件型別
String action = intent.getAction();
if("android.intent.action.SCREEN_OFF".equals(action)){
System.out.println("螢幕鎖屏了");
}
if("android.intent.action.SCREEN_ON".equals(action)){
System.out.println("螢幕解鎖了");
}
}
}
最後需要在Activity銷燬的時候取消廣播接收者的註冊。
@Override
protected void onDestroy() {
//當activity銷燬的時候要取消註冊廣播接收者(否則會發生Activity洩露,當Activity銷燬的時候,依然在註冊)
//當activity銷燬,廣播也失效。
unregisterReceiver(screenBroadCastReciever);
super.onDestroy();
}
問題:為什麼操作特別頻繁的廣播事件要搞一個動態註冊呢?
假如這種操作頻繁的廣播事件可以通過靜態方式註冊,那麼在應用退出後,該廣播接收者仍然生效,如果你的裝置中有1000個應用同時註冊了這種操作頻繁的廣播接收事件,此時我一鎖屏,1000個應用都接收到了這個事件,如果一個應用申請一兆記憶體,那麼瞬間你的裝置將要申請1000M的空間,我此次頻繁發廣播,系統要頻繁申請這1000M記憶體,對於android來說,記憶體是有限的,所以此時通過程式碼註冊,應用退出後就不再接收廣播事件,節省了記憶體。
最後強調一下一個知識點:
靜態註冊應用退出後仍然生效,動態註冊Activity退出後廣播接收者也隨之失效。
如果想要動態註冊的廣播接收者不隨Activity退出而失效,可以把動態註冊廣播接收者程式碼放在服務中。
在服務中註冊廣播接收者
package com.example.registbroadcast;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
public class ScreenService extends Service {
private ScreenReceiver receiver;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
//頻繁操作,動態註冊
//[1]建立ScreenReceiver物件
receiver = new ScreenReceiver();
//[2]獲取IntentFilter例項 目的是新增action
IntentFilter filter=new IntentFilter();
//[3]新增action
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
//[4]動態註冊廣播接收者
registerReceiver(receiver, filter);
super.onCreate();
}
@Override
public void onDestroy() {
//當服務被銷燬的時候 取消註冊廣播接收者
unregisterReceiver(receiver);//取消註冊
super.onDestroy();
}
}