BroadcastReceiver详解
广播的概念
Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。
广播的两种类型
无序广播:所有跟广播的intent匹配的广播接收者都可以收到该广播,并且是没有先后顺序(同时收到)
有序广播:所有跟广播的intent匹配的广播接收者都可以收到该广播,但是会按照广播接收者的优先级来决定接收的先后顺序
优先级的定义:-1000~1000
最终接收者:所有广播接收者都接收到广播之后,它才接收,并且一定会接收
abortBroadCast:阻止其他接收者接收这条广播,类似拦截,只有有序广播可以被拦截
BroadcastReceiver的生命周期:
BroadcastReceiver的生命周期,从对象调用它开始,到onReceiver方法执行完成之后结束。
另外,每次广播被接收后会重新创建BroadcastReceiver对象,并在onReceiver方法中执行完就销毁,如果BroadcastReceiver的onReceiver方法中不能在10秒内执行完成,Android会出现ANR异常。所以不要在BroadcastReceiver的onReceiver方法中执行耗时的操作。
如果需要在BroadcastReceiver中执行耗时的操作,可以通过Intent启动Service来完成。但不能绑定Service。
如果我们在Activity中注册了BroadcastReceiver,当这个Activity销毁的时候要主动撤销注册否则会出现异常
创建BroadcastReceiver的步骤:
第一步:创建BroadcastReceiver的子类:
由于BroadcastReceiver本质上是一种监听器,所以创建BroadcastReceiver的方法也非常简单,只需要创建一个BroadcastReceiver的子类然后重写onReceive (Context context, Intentintent)方法即可。
具体代码如下:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String msg=intent.getExtras().get("msg").toString();
Toast.makeText(context,"intent.getAction()"+intent.getAction().toString(),
Toast.LENGTH_LONG).show();
System.out.println("msg:"+msg);
}
}
第二步:注册BroadcastReceiver
一旦实现了BroadcastReceiver,接下就应该指定该BroadcastReceiver能匹配的Intent即注册BroadcastReceiver。
注册BroadcastReceiver的方式有两种:
第一种是静态注册:
这种方法是在配置AndroidManifest.xml配置文件中注册,通过这种方式注册的广播为常驻型广播,也就是说如果应用程序关闭了,有相应事件触发程序还是会被系统自动调用运行。例如:
第二种是动态注册:
这种方法是通过代码在.Java文件中进行注册。通过这种方式注册的广播为非常驻型广播,即它会跟随Activity的生命周期,所以在Activity结束前我们需要调用unregisterReceiver(receiver)方法移除它。例如:
//通过代码的方式动态注册MyBroadcastReceiver
MyBroadcastReceiver receiver=new MyBroadcastReceiver(); (这里可以写系统的广播接收者重写onReceiver方法就可以)
IntentFilter filter=new IntentFilter();
filter.addAction("android.intent.action.MyBroadcastReceiver");
//注册receiver
registerReceiver(receiver, filter);
实例:第一种静态注册发广播的实例:
另一个类来接收数据:
动态注册广播:
在接收类定义接收的action:
在发广播类动态注册:
取消注册:
有序广播(Ordered Broadcast):
一,优缺点
优点:1,按优先级的不同,优先Receiver可对数据进行处理,并传给下一个Receiver
2,通过abortBroadcast可终止广播的传播
缺点:效率低
二,发送广播的方法:sendOrderedBroadcast()
三,优先接收到Broadcast的Receiver可通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,
下一个Receiver 通过 Bundle bundle=getResultExtras(true)方法获取上一个 Receiver传来的数据
Manifest
1. version="1.0" encoding="utf-8"?>
2.
3. package="com.song"
4. android:versionCode="1"
5. android:versionName="1.0" >
6.
7.
8.
9.
10. android:icon="@drawable/ic_launcher"
11. android:label="@string/app_name" >
12.
13. android:label="@string/app_name"
14. android:name=".C48_BroadcastActivity" >
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
主Activity
1. package com.song;
2. //发送广播,bundle绑上key为a的数据
3. import android.app.Activity;
4. import android.content.Intent;
5. import android.os.Bundle;
6. import android.view.View;
7. import android.view.View.OnClickListener;
8. import android.widget.Button;
9.
10. public class C48_BroadcastActivity extends Activity {
11. /** Called when the activity is first created. */
12. Button button;
13. @Override
14. public void onCreate(Bundle savedInstanceState) {
15. super.onCreate(savedInstanceState);
16. setContentView(R.layout.main);
17. button=(Button)findViewById(R.id.button);
18. button.setOnClickListener(new OnClickListener() {
19.
20. @Override
21. public void onClick(View v) {
22. // TODO Auto-generated method stub
23. Intent intent=new Intent("com.song.123");
24. Bundle bundle=new Bundle();
25. bundle.putString("a", "aaa");
26. intent.putExtras(bundle);
27. //有序广播
28. sendOrderedBroadcast(intent, null);
29. }
30. });
31.
32. }
33. }
Receiver2
[java] view plain copy
1. package com.song;
2. //优先接到广播,bundle绑上key为b的数据
3. import android.content.BroadcastReceiver;
4. import android.content.Context;
5. import android.content.Intent;
6. import android.os.Bundle;
7.
8. public class MyReceiver2 extends BroadcastReceiver{
9.
10. @Override
11. public void onReceive(Context context, Intent intent) {
12. // TODO Auto-generated method stub
13. System.out.println("receiver2");
14. // context.getSystemService(name);
15. Bundle bundle=intent.getExtras();
16. bundle.putString("b", "bbb");
17. System.out.println("a="+bundle.get("a"));
18. setResultExtras(bundle);
19. //切断广播
20. // abortBroadcast();
21. }
22.
23. }
Receiver1
[java] view plain copy
1. package com.song;
2. //接收从receiver2传来的广播,包含key为a和b的数据
3. import android.content.BroadcastReceiver;
4. import android.content.Context;
5. import android.content.Intent;
6. import android.os.Bundle;
7.
8. public class MyReceiver1 extends BroadcastReceiver{
9.
10. @Override
11. public void onReceive(Context context, Intent intent) {
12. // TODO Auto-generated method stub
13. System.out.println("receiver1");
14. //要不要接受上一个广播接收器receiver2传来的的数据
15. Bundle bundle=getResultExtras(true);
16. System.out.println("a="+bundle.getString("a")+",b="+bundle.getString("b"));
17. }
18.
19. }
程序效果
EventBus
事件总线管理:将事件放到队列里,用于管理和分发,保证应用的各个部分之间高效的通信以及数据、事件分发,以及模块的解耦。
EventBus: 是一个发布 / 订阅的事件总线。是发布者和订阅者模式,可以实现两个并不知晓的组件之间进行相互通信。可以在同一进程内向不同线程发布事件。
EventBus支持的线程:
使用的实例:
1. 注册和取消注册:
2. 发送数据:
接收数据:
用法步骤总结:导入EventBus 的jar包,在需要发送数据的类使用1.EventBus.getDefault().post(new TypeEvent(bean));
2.TypeEvent:是装数据的类。
public class TypeEvent {
private CategoryType type;
public TypeEvent(CategoryType bean) {
// TODO Auto-generated constructor stub
this.type = bean;
}
/**
* @return the type
*/
public CategoryType getType() {
return type;
}
}
3.在接受数据的类注册和取消注册,运用onEventMainThread(TypeEvent event)
接收数据。在onCreate()方法里注册,在onDestroy()方法里取消注册。
EventBus.getDefault().register(this);
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);// 反注册EventBus
}
Android的BroadcastReceiver和EventBus区别总结:
1.总之可以这么认为EventBus能做的BroadcastReciver都能做到,两者都是属于发布/订阅模式
EventBus设计之初是用于同一进程的消息发送和接收
BroadcastReciver可以在不同进程间发送和接收消息
EventBus在同一线程中发布和接收是同步调用,如果发布和订阅在不同线程间调用是异步的。BroadcastReciver则都属于异步调用。
EventBus通过反射来调用订阅方法多少有些性能的缺失但是对于同一进程的消息传递的方便喝代码解藕来说还是值得一用的。
2. 本地广播是相对消耗时间、空间最多的一种方式,系统级的事件都是通过广播来通知的,比如说网络的变化、电量的变化,短信发送和接收的状态,所以,如果与android系统进行相关的通知,还是要选择本地广播;在BroadcastReceiver的 onReceive方法中,可以获得Context 、intent参数,这两个参数可以调用许多的sdk中的方法,而eventbus获得这两个参数相对比较困难;因此广播相对于其他的方式而言,广播是重量级的,消耗资源较多的方式。他的优势体现在与sdk连接紧密,如果需要同 android 交互的时候,广播的便捷性会抵消掉它过多的资源消耗,但是如果不同android交互,或者说,只做很少的交互,使用广播是一种浪费;
EventBus 作为 Android 开发中常用的框架,拥有着许多优点:
· 调度灵活。不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要;通知的优先级,能够保证 Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点,它们使得创建结构良好组织紧密的通知系统成为可能。
· 使用简单。EventBus 的 Subscriber 注册非常简单,调用 eventBus 对象的 register 方法即可,如果不想创建 eventBus 还可以直接调用静态方法 EventBus.getDefault() 获取默认实例,Subscriber 接收到通知之后的操作放在 onEvent 方法里就行了。成为 Publisher 的过程就更简单了,只需要调用合适的 eventBus(自己创建的或是默认的)的 post 方法即可。
· 快速且轻量。作为 github 的明星项目之一, EventBus 的源代码中许多技巧来改善性能,
eventbus的缺点是他的代码逻辑不是很清楚,在 Subscriber 注册的时候,Subscriber 中的方法会被遍历查找以 onEvent 开头的 public 方法。这将带来一些问题,一旦对代码进行混淆,就无法查找到了。好消息是 EventBus 已经打算使用注解来实现,这应该能够解决代码混淆的问题。
但有一个缺点是观察者独有的,那就是观察者可能会造成接口的膨胀。特别是当程序要求大量形式各异的通知,而程序员有没有做出良好的抽象时,代码中会包含大量的接口,接口数量的增长又会带来命名、注释等等一大堆问题。本质上说观察者要求程序员从零开始实现事件的产生、分发与处理过程,这就要求参与者必须对整个通知过程有着良好的理解。当程序代码适量时,这是一个合理的要求,然而当程序太大时,这将成为一种负担。
在Android中广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。
EventBus是一个发布 / 订阅的事件总线。简单点说,就是两人约定好怎么通信,一人发布消息,另外一个约定好的人立马接收到你发的消息。EventBus就可以帮减少很多事,不管你在任何地方任何位置发布一个事件,接收者都能立马接收到你的消息,不用你考虑android子线程操作UI线程的问题。
一、广播作为Android组件间的通信方式,可以使用以下场景:
1、同一app内部的同一组件内的消息通信(单个或多个线程之间);
2、同一app内部的不同组件之间的消息通信(单个进程);
3、同一app具有多个进程的不同组件之间的消息通信;
4、不同app之间的组件之间消息通信;
5、Android系统在特定情况下与App之间的消息通信。
二、以上的场景,在实际应用中的适用性:
1、同一app内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感觉;
2、同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对同一进程,用于处理此类需求非常适合,且轻松。
3、其他情形,由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。
三、BroadcastReceiver的具体实现流程如下:
1、广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
2、广播发送者通过binder机制向AMS发送广播;
3、AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到 BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
4、消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
四、使用EventBus框架具体流程如下:
1、初始化时注册EventBus.getDefault().register(this);
2、用完之后注销EventBus.getDefault().unregister(this);
3、中间过程主要就是消息推送和接收,通过EventBus.getDefault().post(param)推送,通过onEventMainThread(param),onEventPostThread(param),onEventBackgroundThread(param),onEventAsync(param)接收并处理。
由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。显然,整体流程与EventBus非常类似。