Android四大组件之BroadCastReceiver
1. 基本概念
在Android 中,Broadcast 是一種廣泛運(yùn)用的在應(yīng)用程序之間傳輸信息的機(jī)制。而B(niǎo)roadcastReceiver 是對(duì)發(fā)送出來(lái)的Broadcast 進(jìn)行過(guò)濾接受并響應(yīng)的一類組件,是Android 四大組件之一。
廣播接收者(BroadcastReceiver)用于接收廣播的,廣播的發(fā)送是通過(guò)調(diào)用sendBroadcast(Intent),sendOrderedBroadcast(Intent)來(lái)實(shí)現(xiàn)的。通常一個(gè)廣播可以被多個(gè)廣播接收者所接收。
廣播被分為兩種不同的類型:“普通廣播(Normal Broadcasts)”也叫無(wú)序廣播和“有序廣播(OrderedBroadcasts)”。
1、普通廣播是完全異步(就是不會(huì)被某個(gè)廣播接收者終止)的,可以在同一時(shí)刻(邏輯上)被所有接收者接收到(其實(shí)被接收者接收到也是由順序的,接收者配置的優(yōu)先級(jí)越高,越先接收到,也就是說(shuō)廣
播接收者的優(yōu)先級(jí)對(duì)于無(wú)序廣播也是有用的),消息傳遞的效率比較高,但缺點(diǎn)是:接收者不能將處理結(jié)果傳遞給下一個(gè)接收者,并且無(wú)法終止廣播的傳播。
2、有序廣播是按照接收者聲明的優(yōu)先級(jí)別,被接收者依次接收廣播。如:A 接收者的級(jí)別高于B,B的級(jí)別高于C,那么,廣播先傳給A,再傳給B,最后傳給C 。在傳遞的過(guò)程中如果有某個(gè)接收者終止(abortBroadCast)了該廣播,那么后面的接收者就接收不到該廣播。
3、廣播接收者屬于四大組件之一,因此通常需要AndroidManifest.xml 中進(jìn)行注冊(cè),優(yōu)先級(jí)別聲明在intent-filter 元素的android:priority 屬性中,數(shù)越大優(yōu)先級(jí)別越高,取值范圍:-1000 到1000,優(yōu)先級(jí)別也可以調(diào)用IntentFilter 對(duì)象的setPriority()進(jìn)行設(shè)置。
4、有序廣播的接收者可以終止廣播的傳播,廣播的傳播一旦終止,后面的接收者就無(wú)法接收到廣播,有序廣播的接收者可以將數(shù)據(jù)傳遞給下一個(gè)接收者,如:A 得到廣播后,可以往它的結(jié)果對(duì)象中存入數(shù)據(jù),當(dāng)廣播傳給B 時(shí),B 可以從A 的結(jié)果對(duì)象中得到A 存入的數(shù)據(jù)。
5、Context.sendBroadcast() 發(fā)送的是普通廣播,所有訂閱者都有機(jī)會(huì)獲得并進(jìn)行處理。
6、Context.sendOrderedBroadcast() 發(fā)送的是有序廣播,系統(tǒng)會(huì)根據(jù)接收者聲明的優(yōu)先級(jí)別按順序逐個(gè)執(zhí)行接收者,前面的接收者有權(quán)終止廣播(BroadcastReceiver.abortBroadcast()),如果廣播被前面的接收者終
止, 后面的接收者就再也無(wú)法獲取到廣播。對(duì)于有序廣播, 前面的接收者可以將數(shù)據(jù)通過(guò)setResultExtras(Bundle)方法存放進(jìn)結(jié)果對(duì)象,然后傳給下一個(gè)接收者,下一個(gè)接收者通過(guò)代碼:Bundle bundle = getResultExtras(true))可以獲取上一個(gè)接收者存入在結(jié)果對(duì)象中的據(jù)。
2. Android 系統(tǒng)常見(jiàn)的廣播
Android 為了將系統(tǒng)運(yùn)行時(shí)的各種“事件”通知給其他應(yīng)用(或者說(shuō)通知給我們程序員,讓我們程序員好做出相應(yīng)的反應(yīng)。舉個(gè)生活中的例子:比如我們坐火車(chē),當(dāng)前方到達(dá)某站的時(shí)候,火車(chē)乘務(wù)員會(huì)給所有乘客發(fā)送即將到站的廣播,這樣乘客收到廣播后就可以提前準(zhǔn)備下車(chē)),因此內(nèi)置了多種廣播,比如:系統(tǒng)電量的改變、屏幕的鎖屏、網(wǎng)絡(luò)狀態(tài)的改變、接收到新的短信、撥打電話事件、sdcard 的掛載和移除、應(yīng)用的安裝和卸載等等。比如我們開(kāi)發(fā)的在線播放視頻類的APP,那么我們就有必要監(jiān)聽(tīng)網(wǎng)絡(luò)轉(zhuǎn)態(tài)改變的事件廣播,如果用戶的網(wǎng)絡(luò)狀態(tài)從wifi 改變?yōu)榱?G 上網(wǎng),那么應(yīng)該提示用戶是否使用4G 網(wǎng)絡(luò)繼續(xù)播放視頻,如果不提示用戶,那么就可能導(dǎo)致用戶流量被大量使用,一會(huì)兒功夫,用戶可能就要停機(jī)了。
接下來(lái)我們會(huì)用4 個(gè)案例來(lái)演示廣播接收者的使用。
2.1 案例-IP 撥號(hào)器
需求分析
什么是IP 撥號(hào)服務(wù)?我們?yōu)槭裁匆肐P 服務(wù)?所謂的IP 撥號(hào)就是通過(guò)接入數(shù)據(jù)網(wǎng)絡(luò)來(lái)傳播語(yǔ)音信息。IP 撥號(hào)的目的在于轉(zhuǎn)接至其他頻道,減少話費(fèi)等用處。移動(dòng)17951,聯(lián)通17911,打長(zhǎng)途時(shí)在電話號(hào)碼前加上這個(gè)就便宜了,如果你的手機(jī)上有這個(gè)鍵的話,那么打電話時(shí)輸入長(zhǎng)途電話號(hào)碼后,直接按那個(gè)鍵就撥出去了,它會(huì)自動(dòng)加上IP。通俗的說(shuō)就是打長(zhǎng)途便宜。
例如手機(jī)撥打長(zhǎng)途電話:
移動(dòng)撥區(qū)號(hào)+電話號(hào)=0.25/分市話+0.7/分長(zhǎng)途=0.95/分;
移動(dòng)撥17951+區(qū)號(hào)+電話號(hào)=0.25/分市話+0.3/分長(zhǎng)途=0.55/分。
了解了IP 撥號(hào)的用途之后,接下來(lái),我們通過(guò)程序在用戶撥出去的號(hào)碼前自動(dòng)加上一個(gè)IP 號(hào)碼,為用戶省錢(qián)。
之所以能實(shí)現(xiàn)這樣的功能,是因?yàn)閾芴?hào)的時(shí)候Android 系統(tǒng)會(huì)發(fā)送一個(gè)有序廣播,該廣播中攜帶了用戶撥打的號(hào)碼,我們通過(guò)注冊(cè)廣播接收者就可以獲取到該廣播,同時(shí)將該廣播中的數(shù)據(jù)進(jìn)行修改。從而實(shí)現(xiàn)了用戶號(hào)碼自動(dòng)加IP 號(hào)的功能。
為了能讓用戶自己決定IP 號(hào)碼,我們需要一個(gè)界面(如圖1-2),讓那個(gè)用戶輸入IP 號(hào)碼,然后將該IP 號(hào)碼保存到SharedPreferences 中。
布局文件如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditText android:id="@+id/et_ip"android:hint="請(qǐng)輸入IP 號(hào)碼,默認(rèn)17951"android:layout_width="match_parent"android:layout_height="wrap_content" /><Button android:onClick="saveIP"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="保存"/></LinearLayout>實(shí)現(xiàn)代碼
在該案例中總共用到了兩個(gè)類一個(gè)是MainActivity.java 負(fù)責(zé)讓用戶輸入IP 號(hào)碼,另外一個(gè)是自定義的廣播接收者IPCallerReceiver 負(fù)責(zé)監(jiān)聽(tīng)用戶的撥打電話事件。
編寫(xiě)自定義廣播接收者需要自定義一個(gè)類然后繼承系統(tǒng)提供的BroadCastReceiver 類,然后覆寫(xiě)抽象方法onReceive。
package com.itheima.android.ipcaller;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.SharedPreferences;import android.text.TextUtils;import android.util.Log;//自定義廣播接收者public class IPCallerReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {//獲取數(shù)據(jù)String resultData = getResultData();Log.d("tag", "接收到廣播:"+resultData);//從SharedPreferences 中獲取用戶保存的IP 號(hào)碼SharedPreferences sp =context.getSharedPreferences("config", Context.MODE_PRIVATE);String ipNum = sp.getString("ip", "17951");if (!TextUtils.isEmpty(ipNum)) {//修改數(shù)據(jù)resultData = ipNum+resultData;}//將修改后的數(shù)據(jù)設(shè)置出去setResultData(resultData);}}在清單文件中進(jìn)行注冊(cè)
廣播是Android 四大組件之一,因此需要在AndroidManifest.xml 中進(jìn)行注冊(cè)。同時(shí)監(jiān)聽(tīng)用戶的撥打電話行為也屬于侵犯用戶隱私的行為,因此需要添加權(quán)限。
注冊(cè)廣播
<receiver android:name="com.itheima.android.ipcaller.IPCallerReceiver"><intent-filter ><action android:name="android.intent.action.NEW_OUTGOING_CALL"></action></intent-filter></receiver>大家可以發(fā)現(xiàn)廣播接收者的注冊(cè)也需要通過(guò)intent-filter 來(lái)監(jiān)聽(tīng)特定的廣播,如果是監(jiān)聽(tīng)Android 系統(tǒng)的,那么在action 中就需要配置系統(tǒng)提供的常量。如果監(jiān)聽(tīng)自定義發(fā)送的廣播,那么就需要配置自定義廣播設(shè)置的action。
聲明權(quán)限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>2.2 案例-短信監(jiān)聽(tīng)器
系統(tǒng)接收到短信時(shí)會(huì)將該事件以有序廣播(部分自定義的ROM 可能已經(jīng)修改了這個(gè)策略,比如:小米的MIUI 系統(tǒng)) 的形式發(fā)送出去, 因此我們只需要自定義一個(gè)BroadCastReceiver 監(jiān)聽(tīng)該廣播(android.provider.Telephony.SMS_RECEIVED)即可監(jiān)聽(tīng)到短信的到來(lái)。由于該廣播是有序的,因此如果將我們自定義的BroadCastReceiver 配置了較高的優(yōu)先級(jí),那么我們就能先于系統(tǒng)短信app 接收到該廣播,
然后終止該廣播,從而就實(shí)現(xiàn)了短信攔截功能。
通過(guò)該案例我們可以學(xué)到:
- 什么是有序廣播?
- 如何終止有序廣播
- 如何從廣播中獲取短信
- 廣播的優(yōu)先級(jí)概念
在該案例中我們要做一個(gè)類似短信黑名單的應(yīng)用,主界面提供一個(gè)EditText 和一個(gè)Button,讓用戶輸入一個(gè)“黑名單”,點(diǎn)擊保存之后,如果該號(hào)碼發(fā)短信過(guò)來(lái),那么我們的應(yīng)用就將其攔截
2.3 案例-監(jiān)聽(tīng)?wèi)?yīng)用的安裝和卸載
在Android 系統(tǒng)中,安裝應(yīng)用和卸載應(yīng)用事件也都會(huì)發(fā)送特定的廣播,我們可以通過(guò)監(jiān)聽(tīng)這些廣播間接獲取到用戶新安裝了什么軟件,卸載了哪些軟件,進(jìn)而可以統(tǒng)計(jì)用戶的偏好,或統(tǒng)計(jì)某個(gè)軟件的存留率。
需求很簡(jiǎn)單,監(jiān)聽(tīng)?wèi)?yīng)用的安裝和卸載,并將其報(bào)名打印出來(lái)即可。
該應(yīng)用不需要界面,代碼也很簡(jiǎn)單,只需要一個(gè)自定義廣播接收這就可以了。
3. 發(fā)送無(wú)序廣播
4. 發(fā)送有序廣播
5. 特殊的廣播接收者-鎖屏與解屏
攔截短信的廣播
private class InnerSmsReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {System.out.println("InnerSmsReceiver");// 獲取到短信Object[] objects = (Object[]) intent.getExtras().get("pdus");for (Object obj : objects) {SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj);// 獲取到短信內(nèi)容String body = smsMessage.getMessageBody();// 獲取到電話號(hào)碼String phone = smsMessage.getDisplayOriginatingAddress();// 根據(jù)電話號(hào)碼查詢攔截的模式String mode = dao.findNumberMode(phone);/*** 黑名單的攔截模式1 全部攔截(電話攔截+ 短信攔截) 2 電話攔截3 短信攔截*/if ("1".equals(mode) || "3".equals(mode)) {System.out.println("被哥攔截了");//往短信攔截?cái)?shù)據(jù)庫(kù)里面添加數(shù)據(jù)abortBroadcast();}/*** 根據(jù)內(nèi)容攔截(智能攔截)*/if (body.contains("xue sheng mei")) {System.out.println("學(xué)生妹被攔截了");abortBroadcast();}}} }注冊(cè)靜態(tài)廣播
receiver = new InnerSmsReceiver(); IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); // 設(shè)置優(yōu)先級(jí) filter.setPriority(2147483647); // 注冊(cè)一個(gè)短信監(jiān)聽(tīng)的廣播 registerReceiver(receiver, filter);反注冊(cè)廣播,防止內(nèi)存泄露
public void onDestroy() {super.onDestroy();// 反注冊(cè)unregisterReceiver(receiver);receiver = null;// 當(dāng)不用了。設(shè)置為nullmTelephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);listener = null; }注冊(cè)廣播并設(shè)置優(yōu)先級(jí)
<!-- 攔截黑名單信息--> <receiver android:name="com.itheima.mobilesafe_sh2.receiver.InnerSmsReceiver " ><intent-filter android:priority="1000" ><action android:name="android.provider.Telephony.SMS_RECEIVED"/></intent-filter> </receiver>案例1:IP撥號(hào)器
public class CallReceiver extends BroadcastReceiver {//接收到廣播時(shí)就會(huì)調(diào)用@Overridepublic void onReceive(Context context, Intent intent) {//添加IP線路//在打電話廣播中,會(huì)攜帶撥打的電話的號(hào)碼,通過(guò)以下代碼獲取到String number = getResultData();if(number.startsWith("0")){SharedPreferences sp = context.getSharedPreferences("ip", Context.MODE_PRIVATE);String ipNumber = sp.getString("ipNumber", "");//把IP線路號(hào)碼添加至用戶撥打號(hào)碼的前面number = ipNumber + number;//把新的號(hào)碼重新放入廣播中setResultData(number);abortBroadcast();} } } public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void click(View v){EditText et = (EditText) findViewById(R.id.et);SharedPreferences sp = getSharedPreferences("ip", MODE_PRIVATE);sp.edit().putString("ipNumber", et.getText().toString()).commit();} }案例2:短信防火墻
public class SmsReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {//拿到短信的信息//短信內(nèi)容封裝在intent中Bundle bundle = intent.getExtras();//以pdus為鍵,取出一個(gè)object數(shù)組,數(shù)組中的每一個(gè)元素,都是一條短信Object[] objects = (Object[]) bundle.get("pdus");//拿到廣播中的所有短信for (Object object : objects) {//通過(guò)pdu來(lái)構(gòu)造短信SmsMessage sms = SmsMessage.createFromPdu((byte[])object);if(sms.getOriginatingAddress().equals("138438")){//阻止其他廣播接收者收到這條廣播abortBroadcast(); // SmsManager.getDefault().sendTextMessage(sms.getOriginatingAddress(), null, "你是個(gè)好人", null, null);} // System.out.println(sms.getMessageBody()); }} }案例3:監(jiān)聽(tīng)SD卡狀態(tài)
public class SDStatusReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {//判斷收到的到底是什么廣播String action = intent.getAction();if("android.intent.action.MEDIA_MOUNTED".equals(action)){Toast.makeText(context, "SD卡可用", 0).show();}else if("android.intent.action.MEDIA_REMOVED".equals(action)){Toast.makeText(context, "SD卡拔出", 0).show();}else if("android.intent.action.MEDIA_UNMOUNTED".equals(action)){Toast.makeText(context, "SD卡不可用", 0).show();}} }案例4:手機(jī)勒索軟件
public class BootReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 啟動(dòng)Activity,實(shí)現(xiàn)開(kāi)機(jī)自動(dòng)啟動(dòng)勒索軟件Intent it = new Intent(context, MainActivity.class);//創(chuàng)建任務(wù)棧存放啟動(dòng)的Activityit.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(it);} }案例5:監(jiān)控應(yīng)用的狀態(tài)
public class APPStatusReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubString action = intent.getAction();Uri uri = intent.getData();if("android.intent.action.PACKAGE_ADDED".equals(action)){Toast.makeText(context, uri.toString() + "被安裝了", 0).show();}if("android.intent.action.PACKAGE_REPLACED".equals(action)){Toast.makeText(context, uri.toString() + "被升級(jí)了", 0).show();}if("android.intent.action.PACKAGE_REMOVED".equals(action)){Toast.makeText(context, uri.toString() + "被卸載了", 0).show();}} }案例6:發(fā)送自定義廣播
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void click(View v){//發(fā)送自定義廣播Intent intent = new Intent();//廣播中的action也是自定義的intent.setAction("com.itheima.zdy");sendBroadcast(intent);} }總結(jié)
以上是生活随笔為你收集整理的Android四大组件之BroadCastReceiver的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 博客编辑神器:Markdown编辑器
- 下一篇: Android应用开发:页面跳转和数据传