BroadcastReceiver除了接收用户所发送的广播消息之外,还有一个重要的用途:接收系统广播。如果应用需要在系统特定时刻执行某些操作,就可以通过监听系统广播来实现。Android的大量系统事件都会对外发送标准广播。以下是Android常见的广播Action常量(详请参考Android API文档中关于Intent的说明)。
Action常量 |
说明 |
ACTION_TIME_CHANGED |
系统时间被改变。 |
ACTION_DATE_CHANGED |
系统日期被改变。 |
ACTION_TIMEZONE_CHANGED |
系统时区被改变。 |
ACTION_BOOT_COMPLETED |
系统启动完成。 |
ACTION_PACKAGE_ADDED |
新的应用程序被安装。 |
ACTION_PACKAGE_CHANGED |
应用程序被改变。 |
ACTION_PACKAGE_REMOVED |
应用程序被卸载。 |
ACTION_PACKAGE_RESTARTED |
应用程序被重新启动。 |
ACTION_PACKAGE_DATA_CLEARED |
应用程序数据被清理。 |
ACTION_BATTERY_CHANGED |
电池电量改变。 |
ACTION_SHUTDOWN |
系统被关闭。 |
ACTION_BATTRY_LOW |
电池电量低。 |
ACTION_POWER_CONNECTED |
外接电源被连通。 |
ACTION_POWER_DISCONNECTED |
外接电源被断开。 |
ACTION_SHUTDOWN |
系统关闭。 |
ACTION_NEW_OUTGOING_CALL |
播出电话。 |
ACTION _PHONE_STATE |
系统通话状态改变。 |
通过 来监听特殊的广播,即可以实现应用跟随系统执行特定的某些操作。
实例一:开机自动运行的Service
在实际应用总往往会让一些应用跟随系统启动,比如一个黑名单来电自动拦截的APP,监听垃圾短信的APP... ...等等。为了让Service能跟随系统启动,我们需要让BroadcastReceiver监听Action名为:ACTION_BOOT_COMPLETED常量的Intent,然后在BroadcastReceiver启动特定的Service即可。
程序清单:
public class LaunchWithSys extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Intent launchService=new Intent(context, LaunchService.class); //启动指定Service context.startActivity(launchService); } }
从上面看该LaunchWithSys非常简单,只需要启动指定的Service即可,接下来需要注册用于监听系统开机广播的BroadcastReceiver:因此需要在AndroidManifest.xml注册一个下面的接收器:
<!—注册一个监听系统开机广播的BroadcastReceiver à <receiver android:name=”com.jph.monitorbroadcastfromsys.LaunchWithSys”> <intent-filter> <action android:name=”android.intent.action.BOOT_COMPLETED”></action> </intent-filter> </receiver>
除此之外,为了能够让程序访问开机启动事件,需要为程序添加如下的权限:
<pre name="code" class="html"><!—为程序添加访问系统开机事件的权限 à <uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”/>
实例二:短息提醒
在Android中当系统收到短信后会发出一个有序的广播,该广播的Intent的Action为android.provider.Telephony.SMS_RECEIVED。因此只需要在程序中开发一个对应的BroadcastReceiver即可监听到系统接收到的短信。
程序清单:
public class SmsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub //如果收到短信 if (intent.getAction().equals(“android.provider.Telephony.SMS_RECEIVED”)) { //取消这条有序广播(取消后会让其它应用收不到短信) abortBroadcast(); Bundle bundle=intent.getExtras(); if (bundle!=null) {//如果数据不为空 //获得收到的短信数据 Object[] objArray=(Object[]) bundle.get(“pdus”); //根据objArray的大小创建一个SmsMessage数组,用于封装短信内容 SmsMessage []smsMessage=new SmsMessage[objArray.length]; StringBuffer sb=new StringBuffer(); sb.append(“时间:”+new DateFormat().format(“yyyy-MM-dd hh.mm.ss”, new Date())); //遍历smsMessage数组取出所有短信 for (int i = 0; i < smsMessage.length; i++) { //将每条字节类型的短信数据转换成SmsMessage对象 smsMessage[i]=SmsMessage.createFromPdu((byte[])objArray[i]); //获取短信发送地址 sb.append(“发送者:”+smsMessage[i].getOriginatingAddress()); //获取短信内容 sb.append(“短信内容:”+smsMessage[i].getDisplayMessageBody()+”\n”); } Toast.makeText(context, sb.toString(), Toast.LENGTH_LONG).show(); } } } }
实例分析:
由于接收到的短信内容是以字节数组形式保存的,为了便于用这些数据,需要使用SmsMessage.createFromPdu方法将这些字节数组形式的数据转换成SmsMessage对象。
由于接收器可能收到多条短信,因此,通过“pdus”返回了一个短信数组(byte[])所以需要遍历这个数组取出每一条短信。
由于需要监测系统发出的接收短信的广播,所以在配置文件中需要进行如下配置:
<!—注册一个BroadcastReceiver监听系统收到短信发出的广播 à <receiver android:name=”com.jph.monitorbroadcastfromsys.SmsReceiver”> <!—给接收器设个较高的优先级 以便能在其它程序收到广播前结束广播à <intent-filter android:priority=”666”> <action android:name=”android.provider.Telephony.SMS_RECEIVED”></action> </intent-filter> </receiver>
另外,由于程序需要接收短信,所以需要赋予程序接收短信的权限。
<!—为程序添加接收短信的权限 à <uses-permission android:name=”android.permission.RECEIVE_SMS”/>运行效果图:
实例三:手机电量提醒
当手机电量发生变化是系统会发出,Intent的Action名为ACTION_BATTERY_CHANGED的广播,当手机电量过低时系统会发出Intent的Action名为ACTION_BATTRY_LOW的广播。所以,只需监测对应Intent的BroadcastReceiver便可实现手机低电量提醒的应用。
程序清单:
public class MianActivity extends Activity { TextView show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mian); show=(TextView)findViewById(R.id.show); ShowPowerReceiver showPowerReceiver=new ShowPowerReceiver(); IntentFilter filter=new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); //注册showPowerReceiver registerReceiver(showPowerReceiver, filter); } /** * Describe:</br> * 手机电量提醒 * */ class ShowPowerReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub //判断接收到的是否为电量变化的BroadCast Action if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { //当前电量 int level=intent.getIntExtra("level", 0); //总电量 int scale=intent.getIntExtra("scale", 100); int current=level*100/scale; show.setText("当前电量:"+current+"%"); } } } }
程序运行效果图:
实例四:来去电提醒
当通话状态改变时候系统会发出,Intent的Action名为ACTION _PHONE_STAT的广播,当手机播出电话时系统会发出Intent的Action名为ACTION_NEW_OUTGOING_CALL的广播。所以,只需监测对应Intent的BroadcastReceiver便可实现来去电提醒的应用。
程序清单:
来电提醒 public class InCallReceiver extends BroadcastReceiver { private static Object obj; /** * 创建一个永不关闭的Toast * @param context Context context上下文 * @param msg String msg 消息提示信息 * */ public static void showToast(Context context, String msg) { //创建一个Toast对象 Toast toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT); //设置Toast的显示位置 toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0); try { //从toast对象中获取mTN对象 Field field = toast.getClass().getDeclaredField("mTN"); field.setAccessible(true); obj = field.get(toast); //从TN对象中获得show方法 Method method = obj.getClass().getDeclaredMethod("show", null); //调用TN对象的show方法来显示Toast信息提示框 method.invoke(obj, null); } catch (Exception e) { } } /** * 关闭Toast * */ public static void closeToast() { if (obj != null) { try { Method method = obj.getClass().getDeclaredMethod("hide", null); method.invoke(obj, null); } catch (Exception e) { } } } @Override public void onReceive(final Context context, final Intent intent) { //获取电话管理服务 TelephonyManager tm = (TelephonyManager) context .getSystemService(Service.TELEPHONY_SERVICE); switch (tm.getCallState()) { case TelephonyManager.CALL_STATE_RINGING://响铃 String incomingNumber = intent .getStringExtra("incoming_number"); showToast(context, incomingNumber); break; case TelephonyManager.CALL_STATE_OFFHOOK://接听电话 Log.d("call_state", "offhook"); break; case TelephonyManager.CALL_STATE_IDLE://挂断电话 closeToast(); } } } //去电提醒 public class OutCallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //获取去电的电话号码 String outcomingNumber = intent .getStringExtra(Intent.EXTRA_PHONE_NUMBER); InCallReceiver.showToast(context, outcomingNumber); } }
配置文件:AndroidManifest.xml
<!-- 为程序添加访问电话状态的权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 为程序添加允许程序监视,修改或放弃播出电话 --> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <!-- 注册一个BroadcastReceiver监听系发出的电话状态的广播 --> <receiver android:name=".InCallReceiver" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE" /> </intent-filter> </receiver> <!-- 注册一个BroadcastReceiver监听系统发出的新来电的广播 --> <receiver android:name=".OutCallReceiver" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> </intent-filter> </receiver>程序运行效果图:
最后:由于在接收广播的时候,系统会为每一次接收广播单独创建一个广播接收器,即使是同一个广播的多次接收。因此,当电话处于不同的通话状态时,实际上是在不同的接收器对象中发生的,所以需要使用静态变量来保存Toast对象,不然closeTost我无法获取在上一个状态创建的Toast对象,也就无法关闭Toast信息框了。