业精于勤,荒于嬉;行成于思,毁于随。---韩愈
在Android开发中BroadcastReceiver的使用是非常广泛的它也是Android的四大组件之一,翻译成汉语就是:"广播接收者",那么什么是广播呢?举个日常生活的例子,在农村大队里村干部这里会有一个大喇叭,用来发送广播通知各种事情,假如由于小花超生通知小花和小明来开会村干部就会通过大喇叭说:”喂,咱庄上的人注意了,小花和小明请以最快的速度来开会"。这个广播发出之后,首先这个庄上的所有人都听到了广播,村民们根据广播的内容选择去还是不去,小花和小明听到广播后开始分析:"小花和小明去开会,我是小花,我应该去开会"小明也是如此,村民们虽然听到了广播,但是分析后发现跟我没关系啊,于是继续干自己的事情。这是我们生活中的广播的形式,那么在android中广播是怎么一回事呢?一起来揭开它的面纱吧。
Android中的BroadcastReceiver的设计不得不让人拍案叫绝,它大大减少了开发者的工作量,假如现在要实现功能检测电池的电量少于10%进入省电模式,如果没有广播,我们要设监听,注册监听器,然后处理。这样是不是很麻烦?用广播就很爽啊,当电池的电量改变时系统会发出一条广播我们只需要接收这条广播然后判断它是否低于10%就行了。这样工作量大大的减少了!这只是广播的一个小的应用,除此之外当手机的网络变化、手机开机时等系统也会发出一条广播,我们可以收到此广播做自己想要的处理
下面我们结合实例来对BroadcastReceiver做详细的阐述,如有疑问欢迎提问,如有谬误欢迎批评指正,谢谢
一、 广播分类
1.普通广播
普通广播是完全异步的,所谓异步就是说这个广播可以在同一时刻(逻辑上)被所有广播接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将接收到的数据处理后将处理结果传递给下一个接收者,并且无法终止广播Intent的传播;
这种广播的发送方式是:Context.SendBroadcast();
2.有序广播
然而有序广播是按照接收者声明的优先级别声明方式如下
<receiver
android:name=".broadcastreceiver.FirstBroadcastReceiver"> <intent-filter android:priority="Integer" > <action android:name="com.example.BroadcastReceiver" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
在intent-filter元素的android:priority属性中配置优先级,数越大优先级别越高,取值范围:-1000到1000。也可以调
用IntentFilter对象的setPriority()进行设置,配置好优先级后当广播过来时被接收者依次接收广播。假如现在现在有三个接收者Receiver1、Receiver2、Receiver3
如:Receiver1的级别高于Receiver2,Receiver2的级别高于Receiver3,那么当发送一个有序广播的时候,广播会先发给Receiver1,然后再发给Receiver2,最后传给Receiver3。Receiver1收到广播后可以对广播进行处理:
(1)可以往广播里存入数据,当广播传给Receiver2时,Receiver2可以从广播中得到Receiver1存入的数据。
(2)中断广播调用BroadcastReceiver.abortBroadcast()这个方法进行广播的中断,中断后Receiver2、Receiver3将收不到此条广播
这种广播的发送的方式是:Context.sendOrderedBroadcast();
按来源分(这是我自己的想的,哈哈)
1.自己定义的广播
2.系统广播就是系统里面自带的广播
二、广播的注册方式
广播的注册方式可以分为两种
1.静态注册
这种方式是在AndroidManifest.xml清单文件中注册的注册方式如下
<receiver
android:name = ".broadcastreceiver.FirstBroadcastReceiver"> <intent-filter> < action android:name ="com.example.BroadcastReceiver" /> < category android:name ="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
配置了以上信息之后,只要是com.example.BroadcastReceiver这个地址的广播,广播接收者都能够接收的到。注意,这种方式的注册是常驻型的,也就是说当应用关闭后,如果有广播信息传来,广播接受者也会被系统调用而自动运行。
2.动态注册
动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service(在服务中监听网络的状态,电量的变化,然后在服务中发出一个广播)注册一个广播,
注:动态注册的广播是非常驻型广播,当应用程序结束了,广播自然就没有了,比如你在activity中的onCreate或者onResume中注册广播,同时你必须在onDestory或者onPause中取消广播订阅。不然会报异常。
注册广播的代码如下
FirstBroadcastReceiver firstBroadcastReceiver= new FirstBroadcastReceiver(); IntentFilter intentFilter= new IntentFilter(); intentFilter.addAction( "com.example.BroadcastReceiver" ); registerReceiver(firstBroadcastReceiver,intentFilter);
可能有的朋友会问,为什么registerReceiver和SendBroadcastReceiver在Activity和Service中可以直接用,我们看看它们的继承关系图:
通过这四张图看明白了吧,registerReceiver和sendBroadcast是android.content.ContextWrapper类中的方法,Activity和Service都继承了ContextWrapper,所以可以直接调用,但是如果我们在其它的类中的话是不可以直接调用的可以通过Conext.sendBroadcast的方式进行调用。
还有一点需要注意,当我们的在Activity或Service中执行了销毁操作但是没有解除注册的话会抛异常,虽然不影响程序的运行,但是作为一名有经验的开发人员,我们要保证代码的严谨性(说这话没别的意思,就是装个B,哈哈)抛出的异常提示是否忘记调用unregisterReceiver()方法了,异常如下
所以我们要根据需要在合适的地方解除注册假如我们在onDestroy中解除注册代码如下
@Override protected void onDestroy() { super.onDestroy(); unregisterReceiver( firstBroadcastReceiver ); }
下面我们通过实例来对上面的理论进行测试,来进一步的加深印象,看看广播是怎么发出的,是怎么被接收的
三、BroadCastReceiver实例详解
1.关于静态和动态注册广播的讨论
首先创建BroadcastReceiver
在创建BroadcastReceiver时需要声明一个类来继承android.content.BroadcastReceiver包下的BroadcastReceiver并且复写其中的onReceive方法示例代码
package com.example.broadcastreceiverpractice.broadcastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class FirstBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "FirstBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = intent.getStringExtra("key" ); Log. i( TAG,getFromBroadcast); } }
在onReceive方法中我们通过获得广播传过来的数据来进行测试是否收到了我们所需要的广播,进行到这儿还有更重要的一步,那就是注册广播假如我们不注册广播的话,我们想向这个广播接收者发消息的话就无法发送,因为找不到和这个广播对应的东西,注册广播可以分为两种上面已经提到了我们再来通过实例说一遍
<receiver
android:name =".broadcastreceiver.FirstBroadcastReceiver" > <intent-filter> <action android:name ="com.example.BroadcastReceiver" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
配置以上内容的作用就是说从当前Receiver只接收从com.example.BroadcastReceiver发过来的广播,这样广播发送者和接收者就建立了联系了。当从com.example.BroadcastReceiver发出广播时系统就会自动调用这个广播接收者来进行相关的操作
说了这么多我们在Activity中测试一下吧
package com.example.broadcastreceiverpractice; 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 MainActivity extends Activity { private Button btn_send; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout. activity_main); btn_send=(Button) findViewById(R.id. btn_send); btn_send.setOnClickListener( new OnClickListener() { public void onClick(View v) { //发送一条广播com.example.BroadcastReceiver要和广播接收者在清单文件中配置的相同 Intent intent = new Intent("com.example.BroadcastReceiver" ); intent.putExtra( "key", "MainActivity中发了一条广播" ); sendBroadcast(intent); } }); } }
我们点击几次发送按钮在广播接收者中打印内容如下
说明我们已经接收到了广播
(2)动态注册
在原来的基础上我们把AndroidManifest.xml清单文件中的静态的注册的代码去掉
只去掉<intent-filter></intent-filter>之间的内容即可,广播接收者是四大组件我们必须注册,大家要把广播接收者是四大组件之一的注册和为了接收自己设定的地址发过来的广播而进行的注册区分开。
package com.example.broadcastreceiverpractice; import com.example.broadcastreceiverpractice.broadcastreceiver.FirstBroadcastReceiver; 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 MainActivity extends Activity { private Button btn_send; private FirstBroadcastReceiver firstBroadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout. activity_main); firstBroadcastReceiver = new FirstBroadcastReceiver(); //声明过滤器 IntentFilter intentFilter= new IntentFilter(); intentFilter.addAction( "com.example.BroadcastReceiver" ); //将添加的动作和声明的广播接收者绑定到一起,和我们在清单文件中配置的作用是一样的 registerReceiver( firstBroadcastReceiver,intentFilter); btn_send=(Button) findViewById(R.id. btn_send); btn_send.setOnClickListener( new OnClickListener() { public void onClick(View v) { //发送一条广播com.example.BroadcastReceiver要和广播接收者在清单文件中配置的相同 Intent intent = new Intent("com.example.BroadcastReceiver" ); intent.putExtra( "key", "MainActivity中发了一条广播" ); sendBroadcast(intent); } }); } @Override protected void onDestroy() { super.onDestroy(); //解除注册 unregisterReceiver( firstBroadcastReceiver); } }
我们点击几次按钮发送广播打印内容如下
关于动态注册广播的特点以及注意事项在"二"中已经说了,这里就不再重复说了唯一要提的一点就是别忘了解除注册。
但是问题来了,上面只是一个广播,假如我们我们有多个广播呢,谁先接收呢,能同时接收吗等等问题,要想弄清这个问题必须掌握普通广播和有序广播
2.有序广播和普通广播
(1)普通广播
要讨论这个问题你我们首先新建三个广播
分别如下
package com.example.broadcastreceiverpractice.broadcastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class FirstBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "FirstBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = intent.getStringExtra("key" ); Log. i( TAG,getFromBroadcast); } }
package com.example.broadcastreceiverpractice.broadcastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class SecondBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "SecondBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = intent.getStringExtra("key" ); Log. i( TAG,getFromBroadcast); } }
package com.example.broadcastreceiverpractice.broadcastreceiver; 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 = "ThirdBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = intent.getStringExtra("key" ); Log. i( TAG,getFromBroadcast); } }
以上三个广播接收者都采用的是静态注册在清单文件中进行注册,然后在MainActivity中点击发送广播按钮打印日志如下
我们看到都收到了,我们再看下时间差那么几毫秒,这个接收顺序跟我们在清单文件中配置的顺序是一致的,假如我们把SecondBroadcastReceiver方法最上面FirstBroadcastReceiver放在第二那么打印日志如下
说明这三个接收者都接收到了广播。
接着我们试着来中断这个广播在每个接收者的的onReceive()方法中来中断广播添加如下代码
abortBroadcast();
然后点击发送按钮发现每个接收者都能接收到广播,说明这种广播是不能被终止的
(2)有序广播
有序广播比较特殊当我们调用Context.sendOrderedBroadcast()发送有序广播时它只发给优先级最高的那个,然后由优先级高的接收者传到优先级低的那里,并且优先级高的接收者有能力中断广播,广播被中断后优先级低的广播将收不到广播。
修改清单文件增加android:priority属性
<receiver android:name =".broadcastreceiver.FirstBroadcastReceiver" > <intent-filter android:priority ="10" > <action android:name ="com.example.BroadcastReceiver" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name =".broadcastreceiver.SecondBroadcastReceiver" > <intent-filter android:priority ="9" > <action android:name ="com.example.BroadcastReceiver" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:name =".broadcastreceiver.ThirdBroadcastReceiver" > <intent-filter android:priority ="8" > <action android:name ="com.example.BroadcastReceiver" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
在上面一中的2提到当优先级高的广播收到广播后一般做两种处理:(1)是把广播中断。(2)接收广播中的数据并按照需求对此数据做更改首先我们来测一下中断广播,我们修改MainActivity的发送广播的方式
//发送一条广播com.example.BroadcastReceiver要和广播接收者在清单文件中配置的相同 Intent intent =new Intent("com.example.BroadcastReceiver" ); intent.putExtra("key", "MainActivity中发了一条广播" ); sendOrderedBroadcast(intent,null);
注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。
所以我们在AndroidMainfest.xml中定义一个权限:
<permission android:protectionLevel="normal" android:name="com.tsingda.broadcast_permission"/>
然后添加此权限
<uses-permission android:name="com.tsingda.broadcast_permission" />
然后在SecondBroadcastReceiver 中调用
abortBroadcast();
这个方法然后点击发送按钮打印日志如下
发现ThirdBroadcastReceiver没有接收到广播,说明中断广播成功。
然后再来看更改广播中的数据,在讲解更改之前我们需要了解setResultExtras(Bundle)这个方法,在进行广播的传输时优先接收到Broadcast的接收者可以通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,下一个接收者通过Bundle
bundle = new getResultExtras(true)可以获取上一个接收者存入的数据。
setResultExtras(Bundle)方法的详细介绍如下图
我们把三个接收者做如下更改
package com.example.broadcastreceiverpractice.broadcastreceiver; 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 = "FirstBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = intent.getStringExtra("key" ); Log. i( TAG,getFromBroadcast); Bundle bundle = new Bundle(); bundle.putString( "key", getFromBroadcast+ "@First"); setResultExtras(bundle); } }
package com.example.broadcastreceiverpractice.broadcastreceiver; 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 = "SecondBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = getResultExtras(true ).getString("key" ); Log. i( TAG,getFromBroadcast); Bundle bundle = new Bundle(); bundle.putString( "key", getFromBroadcast+ "@Second"); setResultExtras(bundle); } }
package com.example.broadcastreceiverpractice.broadcastreceiver; 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 = "ThirdBroadcastReceiver" ; @Override public void onReceive(Context context, Intent intent) { String getFromBroadcast = getResultExtras(true ).getString("key" ); Log. i( TAG,getFromBroadcast); } }
点击发送广播按钮打印日志如下
说明我们接收到了广播并进行了修改然后传到了下一级。好了关于广播接收者的讨论就到这了,如果错误欢迎批评指正,谢谢