Android_Broadcast

来源:互联网 发布:企业出口海关数据 编辑:程序博客网 时间:2024/05/21 04:40

简介

Broadcast Receiver用于接收并处理广播通知(broadcastannouncements)。

多数的广播是系统发起的,如地域变换、电量不足、来电来信等。

程序可以有任意数量的Broadcast Receivers来响应它觉得重要的通知。

Broadcast Receiver可以通过多种方式通知用户:启动activity、使用NotificationManager、开启背景灯、振动设备、播放声音等,最典型的是在状态栏显示一个图标,这样用户就可以点它打开看通知内容。

通常我们的某个应用或系统本身在某些事件(电池电量不足、来电来短信)来临时会广播一个Intent出去,我们可以利用注册一个Broadcast Receiver来监听到这些Intent并获取Intent中的数据。


在程序中接受系统发的广播

我们举一个例子说明,一个接受系统Date改变的广播的例子。

先新建一个HelloBroadcastReceiver.java类,继承自BroadcastReceiver并复写它的onReceive方法。这样就创建了一个专门用来接收广播的类。

package com.tianjf;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;public class HelloBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.d("debug", "Date has been changed!");        Log.d("debug", ""+intent.getAction()); }}
但是上面的Broadcast Receiver没有指定接收哪个广播,我们要指定某个Receiver接收哪个广播,必须在AndroidManifest.xml中注册此Receiver
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.tianjf"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk android:minSdkVersion="4" />    <application        android:icon="@drawable/ic_launcher"        android:label="@string/app_name" >        <activity            android:name=".BroadcastDemoActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <!-- 定义Broadcast Receiver指定监听的Action(系统Date改变action) -->        <receiver android:name="HelloBroadcastReceiver" >            <intent-filter>                <action android:name="android.intent.action.DATE_CHANGED" />            </intent-filter>        </receiver>    </application></manifest>

OK,运行之后,更改系统Date看看,果然打印了两行Log。(着两行Log有时候打得出来,有时候打不出来,不知道神马原因)


接收自己发的广播

直接上代码

BroadcastDemoActivity.java

package com.tianjf;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class BroadcastDemoActivity extends Activity implements OnClickListener {private Button button;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);button = (Button) findViewById(R.id.button);button.setOnClickListener(this);}@Overridepublic void onClick(View v) {// 定义一个intentIntent intent = new Intent();intent.setAction("MY_BROADCAST_ACTION");intent.putExtra("yaoyao","yaoyao is 189 days old ,27 weeks -- 2010-08-10");// 广播出去sendBroadcast(intent);}}
HelloBroadcastReceiver.java
package com.tianjf;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;public class HelloBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.d("debug", "" + intent.getAction());if (intent.getAction().equals("android.intent.action.DATE_CHANGED")) {Log.d("debug", "Date has been changed!");Log.d("debug", "" + intent.getAction());} else if (intent.getAction().equals("MY_BROADCAST_ACTION")) {Log.d("debug", "Say Hello to Yaoyao!");Log.d("debug", intent.getStringExtra("yaoyao"));}}}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.tianjf"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk android:minSdkVersion="4" />    <application        android:icon="@drawable/ic_launcher"        android:label="@string/app_name" >        <activity            android:name=".BroadcastDemoActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <!-- 定义Broadcast Receiver指定监听的Action(系统Date改变action和自动义的广播) -->        <receiver android:name="HelloBroadcastReceiver" >            <intent-filter>                <action android:name="MY_BROADCAST_ACTION" />                <action android:name="android.intent.action.DATE_CHANGED" />            </intent-filter>        </receiver>    </application></manifest>

注册广播的2种方式

BroadcastReceiver在没有注册广播地址之前是使用不了的。没有注册广播地址的BroadcastReceiver就像一个缺少选台按钮的收音机,虽然功能俱备,但也无法收到电台的信号。有两种注册方法:

  1. 静态注册。在AndroidManifest.xml文件中配置(上文两个例子已经介绍了)
  2. 动态注册。要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播

静态注册的方法参照以上两个例子。

至于动态注册的方法,我们看以下例子:

将以上的BroadcastDemoActivity做如下修改

package com.tianjf;import android.app.Activity;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class BroadcastDemoActivity extends Activity implements OnClickListener {private Button button;HelloBroadcastReceiver receiver;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);button = (Button) findViewById(R.id.button);button.setOnClickListener(this);receiver = new HelloBroadcastReceiver();IntentFilter filter = new IntentFilter();filter.addAction("android.intent.action.DATE_CHANGED");filter.addAction("MY_BROADCAST_ACTION");registerReceiver(receiver, filter);}@Overridepublic void onClick(View v) {// 定义一个intentIntent intent = new Intent();intent.setAction("MY_BROADCAST_ACTION");intent.putExtra("yaoyao","yaoyao is 189 days old ,27 weeks -- 2010-08-10");// 广播出去sendBroadcast(intent);}@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(receiver);}}
注意:动态注册广播的方法必须在onDestory方法里面注销,如果不注销的话,系统会报一个异常(如下)
12-19 07:16:04.186: E/ActivityThread(23244): Activity com.tianjf.BroadcastDemoActivity has leaked IntentReceiver com.tianjf.HelloBroadcastReceiver@40cef150 that was originally registered here. Are you missing a call to unregisterReceiver()?
异常也提示了要注销广播。

两种广播注册方式的区别:

  • 静态注册的方法,不管应用处于什么状态,甚至于退出应用,都能接收广播并作相应的处理。当然,相对来说是比较耗电的
  • 动态注册的方法,因为动态注册必须要注销广播,所以,应用退出也就不接收广播了。所以是省电的


有序广播(Ordered Broadcast)

有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播

为了演示有序广播的流程,我们创建三个接收者的代码,如下:

package com.tianjf;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.util.Log;public class FirstBroadcastReceiver extends BroadcastReceiver {private static final String TAG = "TestOrderdBroadcastReceiver";@Overridepublic void onReceive(Context context, Intent intent) {String msg = intent.getStringExtra("msg");Log.i(TAG, "FirstBroadcastReceiver: " + msg);Bundle bundle = new Bundle();bundle.putString("msg", msg + "@FirstBroadcastReceiver");setResultExtras(bundle);}}
package com.tianjf;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.util.Log;public class SecondBroadcastReceiver extends BroadcastReceiver {private static final String TAG = "TestOrderdBroadcastReceiver";@Overridepublic void onReceive(Context context, Intent intent) {String msg = getResultExtras(true).getString("msg");Log.i(TAG, "SecondBroadcastReceiver: " + msg);Bundle bundle = new Bundle();bundle.putString("msg", msg + "@SecondBroadcastReceiver");setResultExtras(bundle);}}
package com.tianjf;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;public class ThirdBroadcastReceiver extends BroadcastReceiver {private static final String TAG = "TestOrderdBroadcastReceiver";@Overridepublic void onReceive(Context context, Intent intent) {String msg = getResultExtras(true).getString("msg");Log.i(TAG, "ThirdBroadcastReceiver: " + msg);}}
我们注意到,在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法将一个Bundle对象设置为结果集对象,传递到下一个接收者那里,这样以来,优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。

之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:

        <receiver android:name=".FirstBroadcastReceiver" >            <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=".SecondBroadcastReceiver" >            <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=".ThirdBroadcastReceiver" >            <intent-filter android:priority="998" >                <action android:name="android.intent.action.MY_BROADCAST" />                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </receiver>
我们看到,现在这三个接收者的<intent-filter>多了一个android:priority属性,并且依次减小。这个属性的范围在-1000到1000,数值越大,优先级越高。

现在,我们需要写一下发送广播的代码,如下:

Intent intent = new Intent("android.intent.action.MY_BROADCAST");      intent.putExtra("msg", "hello receiver.");      sendOrderedBroadcast(intent, "myAndroid.permission.MY_BROADCAST_PERMISSION");
注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。

所以我们在AndroidMainfest.xml中定义一个权限:

    <permission        android:name="myAndroid.permission.MY_BROADCAST_PERMISSION"        android:protectionLevel="normal" />
然后声明使用了此权限:
<uses-permission android:name="myAndroid.permission.MY_BROADCAST_PERMISSION" />

然后我们点击发送按钮发送一条广播,控制台打印如下:

12-20 08:13:45.978: I/TestOrderdBroadcastReceiver(29914): FirstBroadcastReceiver: hello receiver.12-20 08:13:46.088: I/TestOrderdBroadcastReceiver(29914): SecondBroadcastReceiver: hello receiver.@FirstBroadcastReceiver12-20 08:13:46.138: I/TestOrderdBroadcastReceiver(29914): ThirdBroadcastReceiver: hello receiver.@FirstBroadcastReceiver@SecondBroadcastReceiver

我们看到接收是按照顺序的,第一个和第二个都在结果集中加入了自己的标记,并且向优先级低的接收者传递下去。

既然是顺序传递,试着终止这种传递,看一看效果如何,我们修改FirstReceiver的代码,在onReceive的最后一行添加以下代码:

abortBroadcast();
然后再次运行程序,控制台打印如下:
12-20 08:13:45.978: I/TestOrderdBroadcastReceiver(29914): FirstBroadcastReceiver: hello receiver.
此次,只有第一个接收者执行了,其它两个都没能执行,因为广播被第一个接收者终止了。

注:只有有序广播可以用abortBroadcast();来终止,其他广播是没有效果的。


BroadcastReceiver的生命周期

BroadcastReceiver的生命周期很短,也就是onReceive的执行时间,onReceive执行结束,BroadcastReceiver的实例就被销毁(不管那种注册方法)


Broadcast Action 大全

android.intent.action.BATTERY_CHANGED
充电状态,或者电池的电量发生变化

android.intent.action.BOOT_COMPLETED
在系统启动后,这个动作被广播一次(只有一次)

android.intent.action.CFF
语音电话的呼叫转移状态已经改变

android.intent.action.CONFIGURATION_CHANGED
设备的配置信息已经改变,参见 Resources.Configuration

android.intent.action.DATA_ACTIVITY
电话的数据活动(data activity)状态(即收发数据的状态)已经改变

android.intent.action.DATA_STATE
电话的数据连接状态已经改变

android.intent.action.DATE_CHANGED
日期被改变

android.server.checkin.FOTA_CANCEL
取消所有被挂起的 (pending) 更新下载

android.server.checkin.FOTA_INSTALL
更新已经被确认,马上就要开始安装

android.server.checkin.FOTA_READY
更新已经被下载,可以开始安装

android.server.checkin.FOTA_RESTART
恢复已经停止的更新下载

android.server.checkin.FOTA_UPDATE
通过 OTA 下载并安装操作系统更新

android.intent.action.MEDIABUTTON
用户按下了"Media Button"

android.intent.action.MEDIA_BAD_REMOVAL
扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)

android.intent.action.MEDIA_EJECT
用户想要移除扩展介质(拔掉扩展卡)

android.intent.action.MEDIA_MOUNTED
扩展介质被插入,而且已经被挂载

android.intent.action.MEDIA_REMOVED
扩展介质被移除

android.intent.action.MEDIA_SCANNER_FINISHED
已经扫描完介质的一个目录

android.intent.action.MEDIA_SCANNER_STARTED
开始扫描介质的一个目录

android.intent.action.MEDIA_SHARED
扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享

android.intent.action.MEDIA_UNMOUNTED
扩展介质存在,但是还没有被挂载 (mount)

android.intent.action.MWI
电话的消息等待(语音邮件)状态已经改变

android.intent.action.NETWORK_TICKLE_RECEIVED
设备收到了新的网络 "tickle" 通知

android.intent.action.PACKAGE_ADDED
设备上新安装了一个应用程序包

android.intent.action.PACKAGE_REMOVED
设备上删除了一个应用程序包

android.intent.action.PHONE_STATE
电话状态已经改变

android.intent.action.PROVIDER_CHANGED
更新将要(真正)被安装

android.intent.action.PROVISIONING_CHECK
要求 polling of provisioning service 下载最新的设置

android.intent.action.SCREEN_OFF
屏幕被关闭

android.intent.action.SCREEN_ON
屏幕已经被打开

android.intent.action.SERVICE_STATE
电话服务的状态已经改变

android.intent.action.SIG_STR
电话的信号强度已经改变

android.intent.action.STATISTICS_REPORT
要求 receivers 报告自己的统计信息

android.intent.action.STATISTICS_STATE_CHANGED
统计信息服务的状态已经改变

android.intent.action.TIMEZONE_CHANGED
时区已经改变

android.intent.action.TIME_SET
时间已经改变(重新设置)

android.intent.action.TIME_TICK
当前时间已经变化(正常的时间流逝)

android.intent.action.UMS_CONNECTED
设备进入 USB 大容量存储模式。

android.intent.action.UMS_DISCONNECTED
设备从 USB 大容量存储模式退出

android.intent.action.WALLPAPER_CHANGED
系统的墙纸已经改变

android.intent.action.XMPP_CONNECTED
XMPP 连接已经被建立

android.intent.action.XMPP_DI  
XMPP 连接已经被断开