Broadcast Receiver——广播接受者,用来监听系统或者开发者开发的事件做出响应,比如说开机、电池电量的变化、网络状况的变化,客户交互中发出的事件广播,可以通过广播接收并通知用户。
一、广播的功能与特征
1、广播的生命周期很短,从context.sendBroadcast(Intent intent),Broadcast Receiver.onReceiver方法,广播就结束生命周期。
2、与其他四大组件一样,广播也是在同一进程的主线程中完成,是同步并快速的,不推荐在广播中新开子线程,因为往往子线程还未完成任务,广播已经被销毁,如果要进行耗时操作,最好通过intent启动Service,在服务中进行耗时操作。
3、如果onReceiver方法10秒内没有执行完毕任务,Android认为该程序没反应。
二、广播监听的两种方法
广播监听有静态注册和动态注册(在实际开发中,动态注册常用一些,静态注册也必不可少)。
静态注册,将广播接收器的配置信息写在应用的配置文件中(AndroidManifest.xml),当有广播发生时,组件管理服务会从应用安装包管理服务中获取已经安装的广播组件信息。
动态注册,Context.register,动态将广播接收器与所需监听的事件绑定起来。
1.静态注册——跟其他组件一样,写在AndroidManifest.xml文件中application标签之内
<receiver android:name=".ColdReceiver"><!-- 你的Receiver名称 --> <intent-filter> <action android:name="android.intent.action.COLD_BROADCAST"/> <!-- 你广播要接受的intent名称 --> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>然后我们创建一个ColdReceiver类,继承BroadcastRecevier类public class ColdReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //跳转到service中 intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(1); //开启service context.startService(intent); //日志打印 Log.d("TEST","静态注册"); }}上面的Service启动看到了吗,这里我用了上次说过的快捷跳转。其中service的配置和内容如下<service android:name=".BroadcastService"><!-- 你自定义的service文件 (在<application></application>里面加)--> <intent-filter> <action android:name="android.intent.action.BroadcastService" /><!-- 用intent启动时的快捷名(也可以用常规的方式启动) --> <category android:name="android.intent.category.default" /> </intent-filter> </service>public class BroadcastService extends Service{ @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { //开启服务时会首先调用该方法 super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //根据每次intent传过来的信息进行判断来显示不同信息 switch(intent.getFlags()){ case 1:{ Toast.makeText(getApplicationContext(), "静态注册", Toast.LENGTH_SHORT).show(); break; } case 2:{ Toast.makeText(getApplicationContext(), "动态注册", Toast.LENGTH_SHORT).show(); break; } case 3:{ Toast.makeText(getApplicationContext(), "普通广播", Toast.LENGTH_SHORT).show(); break; } case 4:{ Toast.makeText(getApplicationContext(), "有序广播", Toast.LENGTH_SHORT).show(); break; } } return START_STICKY; } @Override public void onDestroy() { // 停止service后会调用此方法 Log.d("TEST", "destroy"); super.onDestroy(); }}那么静态广播的创建就完成了,简单吧,就两个步骤,一是配置广播,二是继承BroadcastReceiver,重写里面的onReceive函数。接下来我们在新建一个工程来检测广播是否可以响应消息。public class MainActivity extends Activity implements OnClickListener{ PRivate Button b1,b2,b3,b4; private Intent intent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intent = new Intent(); //获得界面的控件 b1 = (Button) findViewById(R.id.button1); b1.setOnClickListener(this); b2 = (Button) findViewById(R.id.button2); b2.setOnClickListener(this); b3 = (Button) findViewById(R.id.button3); b3.setOnClickListener(this); b4 = (Button) findViewById(R.id.button4); b4.setOnClickListener(this); Log.d("TEST","===初始化完成==="); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.button1:{//发送到静态注册广播 intent = new Intent("android.intent.action.COLD_BROADCAST"); sendBroadcast(intent); //intent.putExtra("msg", "hello coldreceiver."); break; } case R.id.button2:{//发送到动态注册广播 intent = new Intent("android.intent.action.HOT_BROADCAST"); //intent.putExtra("msg", "hello hotreceiver."); sendBroadcast(intent); break; } case R.id.button3:{//普通广播 intent = new Intent("android.intent.action.NORMAL_BROADCAST"); sendBroadcast(intent); break; } case R.id.button4:{//有序广播 intent = new Intent("android.intent.action.SORT_BROADCAST"); sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION"); break; } } } public void show(String str){ Toast.makeText(this, str, Toast.LENGTH_LONG).show(); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); }}ok,将两个应用都安装到设备上,启动测试用的应用,点击第一个按钮,运行的效果如下
同时,会出现"静态注册"的Toast,可以看出静态注册广播能够跨应用来响应信息,这都要归功于安卓上的组件管理服务,它会读取每个应用的配置文件,然后获取里面的组件信息,每当有消息响应时,组件管理服务会从中查找有没有需要调用的组件,并判断是否进行执行
2、动态注册
动态注册也可以分成两部分,一在代码中进行动态注册,二还是继承BroadcastReceiver,重写里面的onReceive函数。我们在广播应用中新建一个HotReceiver,继承BroadcastReceiver。
public class HotReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //String msg = intent.getStringExtra("msg"); intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(2); context.startService(intent); Log.d("TEST","动态注册"); }}在Activity中进行动态注册public class MainActivity extends Activity { private HotReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //动态注册广播 //新建一个广播对象 receiver = new HotReceiver(); //新建一个intent管理机制,(功能是对组件进行过滤,只获取需要的消息) IntentFilter filter = new IntentFilter(); //添加白名单(只获取该动作的信息) filter.addAction("android.intent.action.HOT_BROADCAST"); //与广播绑定,进行注册 registerReceiver(receiver, filter); } @Override protected void onDestroy() { //取消注册,一定要记得,不然系统会报错误 unregisterReceiver(receiver); stopService(new Intent("android.intent.action.BroadcastService")); super.onDestroy(); }}ok,再使用测试应用来检查一下效果,注意步骤,安装好广播应用打开,不要让它退出,切换到测试用的广播,点击第二个按钮。
测试成功。那么我们关掉广播应用在测试一下,会发现不会再出现动态注册的打印消息。这说明动态注册的广播是与Activity绑定的,当Activity销毁时,广播也会被销毁。在Android中,很多时候最好是使用动态注册的方式使用广播,比如时间变化事件,电量变更事件等,这些事件触发率太高,如果使用静态注册,会导致进程频繁的被构造和销毁从而影响整个系统的效率。
三、广播的两种类型
1、普通广播——普通广播对于多个接收者来说完全是异步的,通常每个接收者无需等待即可接收到广播,接收者相互之间不影响,对于这种广播接收者无法阻止广播,也就是无法阻止其他接收者接受广播。
新建三个静态广播进行测试——调用abortBroadcast阻碍广播向下一级传播
public class NormalReceiver1 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(3); context.startService(intent); Log.d("TEST","普通广播1"); abortBroadcast(); }}public class NormalReceiver2 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(3); context.startService(intent); Log.d("TEST","普通广播2"); abortBroadcast(); }}public class NormalReceiver3 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(3); context.startService(intent); Log.d("TEST","普通广播3"); abortBroadcast(); }}进行配置<receiver android:name=".NormalReceiver1"> <intent-filter> <action android:name="android.intent.action.NORMAL_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name=".NormalReceiver2"> <intent-filter> <action android:name="android.intent.action.NORMAL_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name=".NormalReceiver3"> <intent-filter> <action android:name="android.intent.action.NORMAL_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>安装完毕后,我们点击检测应用的第三个按钮,可以看到
经过测试,abortBroadcast在这里不起作用,并会让系统报错。所以如果要让广播有一定的优先级进行传播就要使用到有序广播。
2、有序广播有序广播通过调用sendOrderedBroadcast函数进行发送。它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播。在有序广播的传递过程中,每个执行中的触发器组件都可以通过BroadcastReceiver.setResult等函数附加额外的数据,而下一个广播则可以使用这些数据(BroadcastReceiver.getResultData)。这样可以构成一个消息数据处理链。为了保证某一事件一定会被处理,可以指明默认的广播接收器(Final Receiver)。一样的,新建三个静态广播样例
public class SortReceiver1 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub //String msg = intent.getStringExtra("msg"); intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(4); context.startService(intent); Log.d("TEST","有序广播1"); abortBroadcast(); }}public class SortReceiver2 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub //String msg = intent.getStringExtra("msg"); intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(4); context.startService(intent); Log.d("TEST","有序广播2"); }}public class SortReceiver3 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub //String msg = intent.getStringExtra("msg"); intent = new Intent("android.intent.action.BroadcastService"); intent.addFlags(4); context.startService(intent); Log.d("TEST","有序广播3"); }}<receiver android:name=".SortReceiver1"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.SORT_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name=".SortReceiver2"> <intent-filter android:priority="999"> <action android:name="android.intent.action.SORT_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name=".SortReceiver3"> <intent-filter android:priority="998"> <action android:name="android.intent.action.SORT_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>我们看到,现在这三个接收者的<intent-filter>多了一个android:priority属性,并且依次减小。这个属性的范围在-1000到1000,数值越大,优先级越高。同样发送广播的代码也是不一样的
sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为 null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信 的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。所以我们在AndroidMainfest.xml中定义一个权限,并获得权限:(是要在广播的应用中声明)<permission android:protectionLevel="normal" android:name="scott.permission.SORT_BROADCAST_PERMISSION" /><uses-permission android:name="scott.permission.SORT_BROADCAST_PERMISSION" />(这里不是写在application内部,而是同application同级)
运行后只会出现这么一个消息:
运行后只执行了第一个广播,因为在第一个广播出我们就终止了广播的继续传递,所以就只会出现这么一条打印消息。四、形形色色的广播(系统发出的广播)
在android中有很多系统自带的intent.action,通过监听这些事件我们可以完成很多功能。
开机:String BOOT_COMPLETED_ACTION 广播:在系统启动后。这个动作被广播一次(只有一次)。监听: “android.intent.action.BOOT_COMPLETED”电话拨入:String ANSWER_ACTION 动作:处理拨入的电话。监听: “android.intent.action.ANSWER”电量变化:String BATTERY_CHANGED_ACTION 广播:充电状态,或者电池的电量发生变化。监听: “android.intent.action.BATTERY_CHANGED”日期改变:String DATE_CHANGED_ACTION 广播:日期被改变。 监听:“android.intent.action.DATE_CHANGED”取消更新下载:String FOTA_CANCEL_ACTION 广播:取消所有被挂起的 (pending) 更新下载。 监听:“android.server.checkin.FOTA_CANCEL”更新开始安装:String FOTA_READY_ACTION 广播:更新已经被下载 可以开始安装。监听 “android.server.checkin.FOTA_READY”主屏幕:String HOME_CATEGORY 类别:主屏幕 (activity)。设备启动后显示的第一个 activity。 监听:"android.intent.category.HOME”新应用:String PACKAGE_ADDED_ACTION 广播:设备上新安装了一个应用程序包。监听: “android.intent.action.PACKAGE_ADDED”删除应用:String PACKAGE_REMOVED_ACTION 广播:设备上删除了一个应用程序包。监听: “android.intent.action.PACKAGE_REMOVED”屏幕关闭:String SCREEN_OFF_ACTION 广播:屏幕被关闭。监听: “android.intent.action.SCREEN_OFF”屏幕开启:String SCREEN_ON_ACTION 广播:屏幕已经被打开。 监听:“android.intent.action.SCREEN_ON”时区改变:String TIMEZONE_CHANGED_ACTION 广播:时区已经改变。监听: “android.intent.action.TIMEZONE_CHANGED”时间改变:String TIME_CHANGED_ACTION 广播:时间已经改变(重新设置)。 “android.intent.action.TIME_SET”时间流逝:String TIME_TICK_ACTION 广播:当前时间已经变化(正常的时间流逝)。 “android.intent.action.TIME_TICK”进入大容量存储模式:String UMS_CONNECTED_ACTION 广播:设备进入 USB 大容量存储模式。 “android.intent.action.UMS_CONNECTED”退出大容量存储模式:String UMS_DISCONNECTED_ACTION 广播:设备从 USB 大容量存储模式退出。 “android.intent.action.UMS_DISCONNECTED”壁纸改变:String WALLPAPER_CHANGED_ACTION 广播:系统的墙纸已经改变。 “android.intent.action.WALLPAPER_CHANGED”web搜索:String WEB_SEARCH_ACTION 动作:执行 web 搜索。 “android.intent.action.WEB_SEARCH”网络变化:String CONNECTIVITY_CHANGE_ACTION 动作:网络变化。“android.intent.action.CONNECTIVITY_CHANGE_ACTION”广播本身的使用很简单,它所包含的内容也很少,但是结合广播来进行应用的开发却会给用户带来更好的体验,广播的使用会让用户觉得开发者能够为用户考虑到各个情况的发生,这点往往能够留住大多数的用户。所以多考虑各种情况的发生,不要吝啬广播的使用。
新闻热点
疑难解答