从现实生活中理解广播机制
一听到广播我们第一感觉就会联想到小时候村里面的广播,每逢村里有什么活动都是通过广播发送的。收听收音机也是一种广播,在收音机中有很多个广播电台,每个广播电台播放的内容都不相同。接受广播时广播(发送方)并不在意我们(接收方)接收到广播时如何处理。好比我们收听交通电台的广播,电台中告诉我们现在在交通状况如何,但它并不关心我们接收到广播时做如何做出处理,这不是广播应该关心的问题,OK,到这里我们从生活中的一些小例子浅浅的理解了一下广播,那么Android 中的广播是如何操作的呢?
Android 的广播机制
在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的处理。下面我画一张粗略的图来帮助大家理解广播的运行机制。
![](http://hi.csdn.net/attachment/201108/8/0_1312771509ffB0.gif)
Android 中有各式各样的广播,各种广播在Android 系统中运行,当系统/应用程序运行时便会向 Android 注册各种广播,Android 接收到广播会便会判断哪种广播需要哪种事件,然后向不同需要事件的应用程序注册事件,不同的广播可能处理不同的事件也可能处理相同的广播事件,这时就需要Android 系统为我们做筛选。
广播被分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播;然而有序广播是按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),被接收者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C。A得到广播后,可以往广播里存入数据,当广播传给B时,B可以从广播中得到A存入的数据。
Context.sendBroadcast()
发送的是普通广播,所有订阅者都有机会获得并进行处理。
Context.sendOrderedBroadcast()
发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast()),如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果通过setResultExtras(Bundle)方法存放进结果对象,然后传给下一个接收者,通过代码:Bundle bundle =getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。
系统收到短信,发出的广播属于有序广播。如果想阻止用户收到短信,可以通过设置优先级,让你们自定义的接收者先获取到广播,然后终止广播,这样用户就接收不到短信了。案例分析:
一个经典的电话黑名单,首先通过将黑名单号码保存在数据库里面,当来电时,我们接收到来电广播并将黑名单号码与数据库中的某个数据做匹配,如果匹配的话则做出相应的处理,比如挂掉电话、比如静音等等。。。
Demo 分析:
下面通过一个小DEMO 来讲解一下广播在Android 中如何编写,在Demo中我们设置了一个按钮为按钮设置点击监听通过点击发送广播,在后台中接收到广播并打印LOG信息。代码如下:
- public class BroadCastActivity extends Activity {
- /** Called when the activity is first created. */
- public static final String ACTION_INTENT_TEST = "com.terry.broadcast.test";
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Button btn = (Button) findViewById(R.id.Button01);
- btn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- Intent intent = new Intent(ACTION_INTENT_TEST);
- sendBroadcast(intent);
- }
- });
- }
- }
接收器代码如下:
- public class MyBroadCast extends BroadcastReceiver {
- public MyBroadCast() {
- Log.v("BROADCAST_TAG", "myBroadCast");
- }
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- Log.v("BROADCAST_TAG", "onReceive");
- }
- }
Android 广播的生命周期
在上面的接收器中,继承了BroadcastReceiver 并重写了它的onReceive 并构造了一个函数,下面通过图片来一步一步认识 Android 广播的生命周期。当我点击一下按钮,它向Android 发送了一个广播,如下图:
![](http://hi.csdn.net/attachment/201108/8/0_1312772331aM1d.gif)
这时我们再点击一下按钮,它还是会再向 Android 系统发送广播,此时日志信息如下:
下面本人画一张图像,描述了Android 中广播的生命周期,其次它并不像Activity 一样复杂,运行原理很简单如下图:
下面来看一下SDK给出的解释:
大意为:如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。
至此,大家应该能明白 Android 的广播生命周期的原理,代码也不用多介绍,很简单的一个发送广播并处理广播的Demo。
Android 如何判断并筛选广播?
前面说过 Android 的广播有各式各样,那么Android 系统是如何帮我们处理我们需要哪种广播并为我们提供相应的广播服务呢?这里有一点需要大家注意,每实现一个广播接收类必须在我们应用程序中的 manifest 中显式的注明哪一个类需要广播,并为其设置过滤器,如下图:
Tip:action 代表一个要执行的动作,在Andriod 中有很action 比如 ACTION_VIEW,ACTION_EDIT
- 在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。
- 使用广播必须要有一个intent 对象必设置其action动作对象
- 使用广播必须在配置文件中显式的指明该广播对象
- 每次接收广播都会重新生成一个接收广播的对象
- 在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理
- 如果在AndroidManifest.xml中注册,当应用程序关闭的时候,也会接收到广播。在应用程序中注册就不产生这种情况了。
下面自己来看一下如何在应用程序中注册BroadcastReceiver
利用代码注册 BroadcastReceiver 时有以下步骤:
1)生成一个 BroadcastReceiver 对象;
2)生成一个 IntentFilter 对象;
3)为 IntentFilter 对象添加一个 Action ;
4)利用 IntentFilter 和 BroadcastReceiver 绑定注册一个 BroadcastReceiver 到系统中,当系统广播该Action事件的时候,就出发该BroadcastReceiver。
应用程序代码:
- public class SMSReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- System.out.println("receive message");
- // 对接受的短消息进行处理
- // 接受 Intent 对象中的数据
- Bundle bundle = intent.getExtras();
- // 在 Bundle 对象当中有一个属性名为 pdus , 这个属性的值是一个 Object数组
- Object[] myObjects = (Object[]) bundle.get("pdus");
- // 创建一个 SmsMessage 类型的数组
- SmsMessage[] message = new SmsMessage[myObjects.length];
- System.out.println(message.length);
- for (int i = 0; i < myObjects.length; i++) {
- // 使用 Object 数组当中的对象创建 SmsMessage 对象
- message[i] = SmsMessage.createFromPdu((byte[]) myObjects[i]);
- System.out.println(message[i].getDisplayMessageBody());
- }
- }
- }
- public class SMSReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- System.out.println("receive message......");
- }
- }
注意:当如果要进行的操作需要花费比较长的时间,则不适合放在BroadcastReceiver中进行处理,引用网上找到的一段解释:
“在 Android 中,程序的响应( Responsive )被活动管理器( Activity Manager )和窗口管理器( Window Manager )这两个系统服务所监视。当 BroadcastReceiver 在 10 秒内没有执行完毕,Android 会认为该程序无响应。所以在 BroadcastReceiver 里不能做一些比较耗时的操作,否侧会弹出ANR ( Application No Response )的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent 给 Service ,由 Service 来完成。而不是使用子线程的方法来解决,因为 BroadcastReceiver 的生命周期很短(在 onReceive() 执行后 BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束BroadcastReceiver 就先结束了。如果 BroadcastReceiver 结束了,它的宿主进程还在运行,那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。 ”
如果要进行的操作占用时间比较长,最好能调用新的Activity或Service进行处理,在调用Intent调用新的 Activity或Service时,其代码如下:
- Intent myIntent = new Intent();
- //通过Intent对象把信息的内容和发送人的号码发给新的Activity
- myIntent.putExtra("SMSBody", strSMSBody);
- myIntent.putExtra("SMSAdress", strSMSAdress);
- //从Service或BroadcastReciver往Activity跳转时,要将Intent的Flag设置为FLAG_ACTIVITY_NEW_TASK才可以
- myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- myIntent.setClass(context, BroadcastActivity.class);
- context.startActivity(myIntent);
这里再添加一点:
在注册广播接收器的时候,静态注册往往比动态注册的优先级别要高。所以要想注册一个电话拦截在最前面,可以在注册一个系统启动广播,当系统启动的时候,系统会发送一个启动广播,然后在广播里注册你所需要的广播接收器;