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