android之BroadcastReceiver應(yīng)用詳解
今天我們來講一下Android中BroadcastReceiver的相關(guān)知識。
BroadcastReceiver也就是“廣播接收者”的意思,顧名思義,它就是用來接收來自系統(tǒng)和應(yīng)用中的廣播。
在Android系統(tǒng)中,廣播體現(xiàn)在方方面面,例如當(dāng)開機完成后系統(tǒng)會產(chǎn)生一條廣播,接收到這條廣播就能實現(xiàn)開機啟動服務(wù)的功能;當(dāng)網(wǎng)絡(luò)狀態(tài)改變時系統(tǒng)會產(chǎn)生一條廣播,接收到這條廣播就能及時地做出提示和保存數(shù)據(jù)等操作;當(dāng)電池電量改變時,系統(tǒng)會產(chǎn)生一條廣播,接收到這條廣播就能在電量低時告知用戶及時保存進度,等等。
Android中的廣播機制設(shè)計的非常出色,很多事情原本需要開發(fā)者親自操作的,現(xiàn)在只需等待廣播告知自己就可以了,大大減少了開發(fā)的工作量和開發(fā)周期。而作為應(yīng)用開發(fā)者,就需要數(shù)練掌握Android系統(tǒng)提供的一個開發(fā)利器,那就是BroadcastReceiver。下面我們就對BroadcastReceiver逐一地分析和演練,了解和掌握它的各種功能和用法。
首先,我們來演示一下創(chuàng)建一個BroadcastReceiver,并讓這個BroadcastReceiver能夠根據(jù)我們的需要來運行。
要創(chuàng)建自己的BroadcastReceiver對象,我們需要繼承android.content.BroadcastReceiver,并實現(xiàn)其onReceive方法。下面我們就創(chuàng)建一個名為MyReceiver廣播接收者:
package com.scott.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class MyReceiver extends BroadcastReceiver { private static final String TAG = "MyReceiver"; @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("msg"); Log.i(TAG, msg); } }
在onReceive方法內(nèi),我們可以獲取隨廣播而來的Intent中的數(shù)據(jù),這非常重要,就像無線電一樣,包含很多有用的信息。
在創(chuàng)建完我們的BroadcastReceiver之后,還不能夠使它進入工作狀態(tài),我們需要為它注冊一個指定的廣播地址。沒有注冊廣播地址的BroadcastReceiver就像一個缺少選臺按鈕的收音機,雖然功能俱備,但也無法收到電臺的信號。下面我們就來介紹一下如何為BroadcastReceiver注冊廣播地址。
靜態(tài)注冊
靜態(tài)注冊是在AndroidManifest.xml文件中配置的,我們就來為MyReceiver注冊一個廣播地址:
<receiver android:name=".MyReceiver"> <intent-filter> <action android:name="android.intent.action.MY_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
配置了以上信息之后,只要是android.intent.action.MY_BROADCAST這個地址的廣播,MyReceiver都能夠接收的到。注意,這種方式的注冊是常駐型的,也就是說當(dāng)應(yīng)用關(guān)閉后,如果有廣播信息傳來,MyReceiver也會被系統(tǒng)調(diào)用而自動運行。
動態(tài)注冊
動態(tài)注冊需要在代碼中動態(tài)的指定廣播地址并注冊,通常我們是在Activity或Service注冊一個廣播,下面我們就來看一下注冊的
代碼:
MyReceiver receiver = new MyReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.MY_BROADCAST"); registerReceiver(receiver, filter);
注意,registerReceiver是android.content.ContextWrapper類中的方法,Activity和Service都繼承了ContextWrapper,所以可以直接調(diào)用。在實際應(yīng)用中,我們在Activity或Service中注冊了一個BroadcastReceiver,當(dāng)這個Activity或Service被銷毀時如果沒有解除注冊,系統(tǒng)會報一個異常,提示我們是否忘記解除注冊了。所以,記得在特定的地方執(zhí)行解除注冊操作:
@Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); }
執(zhí)行這樣行代碼就可以解決問題了。注意,這種注冊方式與靜態(tài)注冊相反,不是常駐型的,也就是說廣播會跟隨程序的生命周期。
我們可以根據(jù)以上任意一種方法完成注冊,當(dāng)注冊完成之后,這個接收者就可以正常工作了。我們可以用以下方式向其發(fā)送一條廣播:
public void send(View view) { Intent intent = new Intent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg", "hello receiver."); sendBroadcast(intent); }
注意,sendBroadcast也是android.content.ContextWrapper類中的方法,它可以將一個指定地址和參數(shù)信息的Intent對象以廣播的形式發(fā)送出去。
點擊發(fā)送按鈕,執(zhí)行send方法,控制臺打印如下:
看到這樣的打印信息,表明我們的廣播已經(jīng)發(fā)出去了,并且被MyReceiver準確無誤的接收到了。
上面的例子只是一個接收者來接收廣播,如果有多個接收者都注冊了相同的廣播地址,又會是什么情況呢,能同時接收到同一條廣播嗎,相互之間會不會有干擾呢?這就涉及到普通廣播和有序廣播的概念了。
普通廣播(Normal Broadcast)
普通廣播對于多個接收者來說是完全異步的,通常每個接收者都無需等待即可以接收到廣播,接收者相互之間不會有影響。對于這種廣播,接收者無法終止廣播,即無法阻止其他接收者的接收動作。
為了驗證以上論斷,我們新建三個BroadcastReceiver,演示一下這個過程,F(xiàn)irstReceiver、SecondReceiver和ThirdReceiver的代碼如下:
package com.scott.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class FirstReceiver extends BroadcastReceiver { private static final String TAG = "NormalBroadcast"; @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("msg"); Log.i(TAG, "FirstReceiver: " + msg); } } public class SecondReceiver extends BroadcastReceiver { private static final String TAG = "NormalBroadcast"; @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("msg"); Log.i(TAG, "SecondReceiver: " + msg); } } public class ThirdReceiver extends BroadcastReceiver { private static final String TAG = "NormalBroadcast"; @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("msg"); Log.i(TAG, "ThirdReceiver: " + msg); } }
然后再次點擊發(fā)送按鈕,發(fā)送一條廣播,控制臺打印如下:
看來這三個接收者都接收到這條廣播了,我們稍微修改一下三個接收者,在onReceive方法的最后一行添加以下代碼,試圖終止廣播:
abortBroadcast();
再次點擊發(fā)送按鈕,我們會發(fā)現(xiàn),控制臺中三個接收者仍然都打印了自己的日志,表明接收者并不能終止廣播。
有序廣播(Ordered Broadcast)
有序廣播比較特殊,它每次只發(fā)送到優(yōu)先級較高的接收者那里,然后由優(yōu)先級高的接受者再傳播到優(yōu)先級低的接收者那里,優(yōu)先級高的接收者有能力終止這個廣播。
為了演示有序廣播的流程,我們修改一下上面三個接收者的代碼,如下:
package com.scott.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; public class FirstReceiver extends BroadcastReceiver { private static final String TAG = "OrderedBroadcast"; @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("msg"); Log.i(TAG, "FirstReceiver: " + msg); Bundle bundle = new Bundle(); bundle.putString("msg", msg + "@FirstReceiver"); setResultExtras(bundle); } }
public class SecondReceiver extends BroadcastReceiver { private static final String TAG = "OrderedBroadcast"; @Override public void onReceive(Context context, Intent intent) { String msg = getResultExtras(true).getString("msg"); Log.i(TAG, "SecondReceiver: " + msg); Bundle bundle = new Bundle(); bundle.putString("msg", msg + "@SecondReceiver"); setResultExtras(bundle); } }
public class ThirdReceiver extends BroadcastReceiver { private static final String TAG = "OrderedBroadcast"; @Override public void onReceive(Context context, Intent intent) { String msg = getResultExtras(true).getString("msg"); Log.i(TAG, "ThirdReceiver: " + msg); } }
我們注意到,在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法將一個Bundle對象設(shè)置為結(jié)果集對象,傳遞到下一個接收者那里,這樣以來,優(yōu)先級低的接收者可以用getResultExtras獲取到最新的經(jīng)過處理的信息集合。
代碼改完之后,我們需要為三個接收者注冊廣播地址,我們修改一下AndroidMainfest.xml文件:
<receiver android:name=".FirstReceiver"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.MY_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name=".SecondReceiver"> <intent-filter android:priority="999"> <action android:name="android.intent.action.MY_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name=".ThirdReceiver"> <intent-filter android:priority="998"> <action android:name="android.intent.action.MY_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
我們看到,現(xiàn)在這三個接收者的<intent-filter>多了一個android:priority屬性,并且依次減小。這個屬性的范圍在-1000到1000,數(shù)值越大,優(yōu)先級越高。
現(xiàn)在,我們需要修改一下發(fā)送廣播的代碼,如下:
public void send(View view) { Intent intent = new Intent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg", "hello receiver."); sendOrderedBroadcast(intent, "scott.permission.MY_BROADCAST_PERMISSION"); }
注意,使用sendOrderedBroadcast方法發(fā)送有序廣播時,需要一個權(quán)限參數(shù),如果為null則表示不要求接收者聲明指定的權(quán)限,如果不為null,則表示接收者若要接收此廣播,需聲明指定權(quán)限。這樣做是從安全角度考慮的,例如系統(tǒng)的短信就是有序廣播的形式,一個應(yīng)用可能是具有攔截垃圾短信的功能,當(dāng)短信到來時它可以先接受到短信廣播,必要時終止廣播傳遞,這樣的軟件就必須聲明接收短信的權(quán)限。
所以我們在AndroidMainfest.xml中定義一個權(quán)限:
<permission android:protectionLevel="normal" android:name="scott.permission.MY_BROADCAST_PERMISSION" />
然后聲明使用了此權(quán)限:
<uses-permission android:name="scott.permission.MY_BROADCAST_PERMISSION" />
然后我們點擊發(fā)送按鈕發(fā)送一條廣播,控制臺打印如下:
我們看到接收是按照順序的,第一個和第二個都在結(jié)果集中加入了自己的標記,并且向優(yōu)先級低的接收者傳遞下去。
既然是順序傳遞,試著終止這種傳遞,看一看效果如何,我們修改FirstReceiver的代碼,在onReceive的最后一行添加以下代碼:
abortBroadcast();
然后再次運行程序,控制臺打印如下:
此次,只有第一個接收者執(zhí)行了,其它兩個都沒能執(zhí)行,因為廣播被第一個接收者終止了。
上面就是BroadcastReceiver的介紹,下面我將會舉幾個常見的例子加深一下大家對廣播的理解和應(yīng)用:
1.開機啟動服務(wù)
我們經(jīng)常會有這樣的應(yīng)用場合,比如消息推送服務(wù),需要實現(xiàn)開機啟動的功能。要實現(xiàn)這個功能,我們就可以訂閱系統(tǒng)“啟動完成”這條廣播,接收到這條廣播后我們就可以啟動自己的服務(wù)了。我們來看一下BootCompleteReceiver和MsgPushService的具體實現(xiàn):
package com.scott.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class BootCompleteReceiver extends BroadcastReceiver { private static final String TAG = "BootCompleteReceiver"; @Override public void onReceive(Context context, Intent intent) { Intent service = new Intent(context, MsgPushService.class); context.startService(service); Log.i(TAG, "Boot Complete. Starting MsgPushService..."); } }
package com.scott.receiver; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class MsgPushService extends Service { private static final String TAG = "MsgPushService"; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate called."); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand called."); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent arg0) { return null; } }
然后我們需要在AndroidManifest.xml中配置相關(guān)信息:
<!-- 開機廣播接受者 --> <receiver android:name=".BootCompleteReceiver"> <intent-filter> <!-- 注冊開機廣播地址--> <action android:name="android.intent.action.BOOT_COMPLETED"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <!-- 消息推送服務(wù) --> <service android:name=".MsgPushService"/>
我們看到BootCompleteReceiver注冊了“android.intent.action.BOOT_COMPLETED”這個開機廣播地址,從安全角度考慮,系統(tǒng)要求必須聲明接收開機啟動廣播的權(quán)限,于是我們再聲明使用下面的權(quán)限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
經(jīng)過上面的幾個步驟之后,我們就完成了開機啟動的功能,將應(yīng)用運行在模擬器上,然后重啟模擬器,控制臺打印如下:
如果我們查看已運行的服務(wù)就會發(fā)現(xiàn),MsgPushService已經(jīng)運行起來了。
2.網(wǎng)絡(luò)狀態(tài)變化
在某些場合,比如用戶瀏覽網(wǎng)絡(luò)信息時,網(wǎng)絡(luò)突然斷開,我們要及時地提醒用戶網(wǎng)絡(luò)已斷開。要實現(xiàn)這個功能,我們可以接收網(wǎng)絡(luò)狀態(tài)改變這樣一條廣播,當(dāng)由連接狀態(tài)變?yōu)閿嚅_狀態(tài)時,系統(tǒng)就會發(fā)送一條廣播,我們接收到之后,再通過網(wǎng)絡(luò)的狀態(tài)做出相應(yīng)的操作。下面就來實現(xiàn)一下這個功能:
package com.scott.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.util.Log; import android.widget.Toast; public class NetworkStateReceiver extends BroadcastReceiver { private static final String TAG = "NetworkStateReceiver"; @Override public void onReceive(Context context, Intent intent) { Log.i(TAG, "network state changed."); if (!isNetworkAvailable(context)) { Toast.makeText(context, "network disconnected!", 0).show(); } } /** * 網(wǎng)絡(luò)是否可用 * * @param context * @return */ public static boolean isNetworkAvailable(Context context) { ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] info = mgr.getAllNetworkInfo(); if (info != null) { for (int i = 0; i < info.length; i++) { if (info[i].getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } }
再注冊一下這個接收者的信息:
<receiver android:name=".NetworkStateReceiver"> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
因為在isNetworkAvailable方法中我們使用到了網(wǎng)絡(luò)狀態(tài)相關(guān)的API,所以需要聲明相關(guān)的權(quán)限才行,下面就是對應(yīng)的權(quán)限聲明:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
我們可以測試一下,比如關(guān)閉WiFi,看看有什么效果。
3.電量變化
如果我們閱讀軟件,可能是全屏閱讀,這個時候用戶就看不到剩余的電量,我們就可以為他們提供電量的信息。要想做到這一點,我們需要接收一條電量變化的廣播,然后獲取百分比信息,這聽上去挺簡單的,我們就來實現(xiàn)以下:
package com.scott.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.BatteryManager; import android.util.Log; public class BatteryChangedReceiver extends BroadcastReceiver { private static final String TAG = "BatteryChangedReceiver"; @Override public void onReceive(Context context, Intent intent) { int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); //當(dāng)前電量 int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); //總電量 int percent = currLevel * 100 / total; Log.i(TAG, "battery: " + percent + "%"); } }
然后再注冊一下廣播接地址信息就可以了:
<receiver android:name=".BatteryChangedReceiver"> <intent-filter> <action android:name="android.intent.action.BATTERY_CHANGED"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
當(dāng)然,有些時候我們是要立即獲取電量的,而不是等電量變化的廣播,比如當(dāng)閱讀軟件打開時立即顯示出電池電量。我們可以按以下方式獲?。?/p>
Intent batteryIntent = getApplicationContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int currLevel = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); int total = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); int percent = currLevel * 100 / total; Log.i("battery", "battery: " + percent + "%");
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android BroadcastReceiver實現(xiàn)網(wǎng)絡(luò)狀態(tài)實時監(jiān)聽
- Android BroadcastReceiver接收收到短信的廣播
- Android運用BroadcastReceiver實現(xiàn)強制下線
- Android BroadcastReceiver廣播注冊方式總結(jié)
- 深入Android中BroadcastReceiver的兩種注冊方式(靜態(tài)和動態(tài))詳解
- Android BroadcastReceiver常見監(jiān)聽整理
- Android BroadcastReceiver廣播機制概述
- Android采取BroadcastReceiver方式自動獲取驗證碼
- 詳解Android中BroadCastReceiver組件
- Android使用BroadcastReceiver監(jiān)聽網(wǎng)絡(luò)連接狀態(tài)的改變
相關(guān)文章
Android studio圓形進度條 百分數(shù)跟隨變化
這篇文章主要為大家詳細介紹了Android studio圓形進度條,百分號跟隨變化,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10務(wù)必掌握的Android十六進制狀態(tài)管理最佳實踐
這篇文章主要為大家介紹了務(wù)必掌握的Android十六進制狀態(tài)管理最佳實踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09Android實現(xiàn)點匯聚成字的動態(tài)效果詳解
在引入?fl_chart?繪制圖表的時候,看到插件有下面這樣的動效,隨機散亂的圓點最后組合成了?Flutter?的?Logo,挺酷炫的。本篇我們來探討類似的效果怎么實現(xiàn)2022-07-07Android自定義View實現(xiàn)圓環(huán)交替效果
這篇文章給大家介紹如何基于Android自定義View實現(xiàn)圓環(huán)交替的效果,實現(xiàn)后效果很贊,有需要的小伙伴們可以參考借鑒。2016-08-08unity5.6 導(dǎo)出gradle工程 Android Studio 導(dǎo)入問題及處理方法
這篇文章主要介紹了unity5.6 導(dǎo)出gradle工程 Android Studio 導(dǎo)入問題及處理方法,需要的朋友可以參考下2017-12-12