BroadcastReceiver详解
![](http://static.blog.csdn.net/images/category_icon.jpg)
版权声明:本文为博主原创文章,未经博主允许不得转载。
前言:这个月写的博客文章数量比较多,因为刚入门的缘故吧,多总结,由浅入深,等后面更深入了再给大家写续篇。
学之广在于不倦,不倦在于固志。 ——晋·葛洪
(学问的渊博在于学习时不知道厌倦,而学习不知厌倦在于有坚定的目标)
一、概述
BroadcastReceiver:直译是“广播接收者”,所以它的作用是用来接收发送过来的广播的。
那我们有必要知道:什么是广播。广播,我的理解就是系统中消息的一种变种;就是当一个事件发生时,比如,系统突然断网,系统就发一个广播消息给所有的接收者,所有的接收者在得到这个消息之后,就知道,啊哦,现在没网络了,我的程序应该怎么办,比如显示默认图片、提示用户等。前面,我们说了,BroadcastReceiver就是一个广播消息接收者。
另外我还要提一下,广播之间信息的传递是通过Intent对象来传递的;在《详解Intent》系列文章中,我讲了,Intent调用分为显示调用的隐式调用两种,由于这里能通知到所有的接收者,所以肯定不能利用显示调用,只有利用隐式调用Intent对象了。(这里的隐式调用,并不是真正意义上的Intent隐式调用,因为Intent隐式调用,当出现很多匹配应用时,会以列表形式提示用户选择一个启动,而这里不同的地方在于,当有很多匹配项时,会给所有的匹配项都发一个消息,我说隐式调用,只是方便大家理解构造Intent的方法,即必须利用构造隐式Intent的方法来构造)
二、注册相关
1、静态注册实例程序
大家可能会问,什么叫静态注册实例程序,先不要管上面的标题,慢慢往下看,后面在讲动态注册时会再提到。
先构造一个接收器:
- public class MyReceiver extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, "MyReceiver:"+msg);
- }
- }
大家可能会想,就这么着,就能收到广播了?当然不是,上面我们说了,通过隐式Intent来发送广播的,我们肯定要匹配这个Intent啊,匹配Intent的术语是Activity中的,在广播这里,叫要注册,也就是要注册一下,什么样的Intent能接收。
MyReceiver的广播接收 注册代码如下:(静态注册)
- <receiver android:name=".MyReceiver">
- <intent-filter>
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
intent-filter标签里,同样是必须的两项:action和category;我在这里自定义了action的名字,等下隐式发送通过时,就是利用匹配action来接收通知的。
此时的AndroidManifest.xml全部内容为:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.test_brocast_blog"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="14" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <receiver android:name=".MyReceiver">
- <intent-filter>
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
完全相同体现在:
- 1、所处位置:都直属<application>标签;
- 2、参数构造基本一样;都有android:name,都有<intent-filter>;
- 3、都是通过Intent传递参数,也都是通过Intent进行匹配!!!!
这说明了一个问题:receiver是activity的变种!!!!!我没有研究源码,但仅从这些相同点来看,他们肯定是从一个共同的类派生出来的。(猜想)
最后是发送广播:
我们在主页面加一个Button,当点击Button时发送广播消息。
布局文件如下:(activity_main.xml)
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.example.test_brocast_blog.MainActivity" >
- <Button
- android:id="@+id/sent_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="发送Broadcast" />
- </RelativeLayout>
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button btn= (Button)findViewById(R.id.sent_btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- send();
- }
- });
- }
- public void send() {
- Intent intent = new Intent("android.intent.action.MY_BROADCAST");
- intent.putExtra("msg", "hello receiver.");
- sendBroadcast(intent);
- }
- }
效果图:
这里注意一下应用名!!!!(com.example.test_brocast_blog)后面会用到。
2、动态注册实例程序
前面,我们说了静态注册,什么叫静态注册呢,就是利用XML来注册。
相反,利用代码来注册的就叫动态注册。
静态注册和动态注册是有区别的,主要体现在接收上。
静态注册的程序,无论该程序是否启动,都会当广播到来时接收,并处理。而动态注册的程序只有在程序运行时才会收到广播消息,程序不运行了,它就收不到了。
动态注册的代码如下:
- MyReceiver receiver = new MyReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction("android.intent.action.MY_BROADCAST");
- registerReceiver(receiver, filter);
注意:发送广播之前,要先注册,不然根本没有接收者匹配,当然,不注册接收者也不会出现任何错误或警告,只是发送一个没有任何接收者的广播播毫无意义。
下面我们新建一个项目,取名叫:Test_Brocast_Blog_Dynamic
同样,写一个MyRecever类来接收广播,MyRecever类内容不变:
- public class MyReceiver extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, msg);
- }
- }
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- //在发送之前,确定在代码的某个位置已经动态注册
- MyReceiver receiver = new MyReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction("android.intent.action.MY_BROADCAST");
- registerReceiver(receiver, filter);
- //发送广播
- Button btn= (Button)findViewById(R.id.sent_btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- send();
- }
- });
- }
- public void send() {
- Intent intent = new Intent("android.intent.action.MY_BROADCAST");
- intent.putExtra("msg", "hello receiver.");
- sendBroadcast(intent);
- }
- }
注:(在运行这个程序之前,先把静态注册的APP装到手机上,这是下面得出结论的前提)
结果如下:
操作界面
结果:
咦?怎么出来两条信息?
也就是说有两个接收者接收到了这条广播,但我们这里只注册了一个MyRecever实例啊。
对的,看应用名称就可以看得出,这是两个不同的应用,有一个是静态注册的(com.example.test_brocast_blog),所以静态注册的程序不管是否启动,都可以收得到匹配的广播的,并对这个广播操作。
如果想试一下,动态注册的代码能不能收到广播,可以反过来一下,运行静态注册的程序,把动态注册的程序关掉,看出来几条Log?答案肯定是一条!因为我们都知道动态注册的代码在程序不运行时是收不到广播的。
三、普通广播与有序广播
普通广播是指大家等级都是一样的,当广播到来时,都能一块接收到,并没有接收的先后顺序。由于是一同接收到的,所以一个接收者是没有办法阻止另一个接收者接收这个广播的。
有序广播是指接收是按一定的优先级顺序来接收的,优先级高的先收到,并可以对广播进行操作后,再传给下一个接收者,当然也可以不传,如果不传的话,后面的接收者就都收不到这个广播了。
普通广播
(在运行这个程序之前,先把手机上前两个APP全部删除,以免影响结果)
首先我们建三个接收者,并对他们全部静态注册。三个接收者的代码分别如下:
FirstRecever:
- public class FirstRecever extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, "FirstRecever:"+msg);
- }
- }
- public class SecondRecever extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, "SecondRecever:"+msg);
- }
- }
MyReceiver
- public class MyReceiver extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, "MyReceiver:"+msg);
- }
- }
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.test_brodcast"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="14" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- 分别注册这三个接收器 -->
- <receiver android:name=".MyReceiver">
- <intent-filter>
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".FirstRecever">
- <intent-filter>
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".SecondRecever">
- <intent-filter>
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button btn= (Button)findViewById(R.id.sent_btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- send();
- }
- });
- }
- public void send() {
- Intent intent = new Intent("android.intent.action.MY_BROADCAST");
- intent.putExtra("msg", "hello receiver.");
- sendBroadcast(intent);
- }
- }
可见,三个全都收到了广播。
有序广播(无访问权限版)
首先有序广播与普通广播的不同点在发送和接收都有不同。
首在发送有序广播:
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button btn= (Button)findViewById(R.id.sent_btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- send();
- }
- });
- }
- public void send() {
- Intent intent = new Intent("android.intent.action.MY_BROADCAST");
- intent.putExtra("msg", "hello receiver.");
- sendOrderedBroadcast(intent, null); //没有添加权限
- }
- }
对这个函数的官方解释是:
public abstract void sendOrderedBroadcast (Intent intent, String receiverPermission)
Broadcast the given intent to all interested BroadcastReceivers, delivering them one at a time to allow more preferred receivers to consume the broadcast before it is delivered to less preferred receivers. This call is asynchronous; it returns immediately, and you will continue executing while the receivers are run.
See BroadcastReceiver
for more information on Intent broadcasts.
接收端
我们上面说了,接收端必须是有序的,是有优先级的,这种优先级是在注册时配置的,比如:
- <receiver android:name=".FirstRecever">
- <intent-filter android:priority="10">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- lt;/receiver>
同样,上面我们三个类FirstRecever、SecondRecever和MyReceiver 的注册文件代码如下 :
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.testbroast_order"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="14" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <receiver android:name=".FirstRecever">
- <intent-filter android:priority="10">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".SecondRecever">
- <intent-filter android:priority="9">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".MyReceiver">
- <intent-filter android:priority="8">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <!-- 接收优先级逐级降低 -->
- </application>
- </manifest>
前面我也曾提到,在一个接收器收到发来的Intent后,可以对其进行更改,对发送来的广播Intent进行修改是利用setResultExtras(bundle); 函数来实现的。
public final void setResultExtras (Bundle extras)
Change the current result extras of this broadcast; only works with broadcasts sent through Context.sendOrderedBroadcast
.
This is a Bundle holding arbitrary data, whose interpretation is up to
the broadcaster. Can be set to null. Calling this method completely
replaces the current map (if any).
This method does not work with non-ordered broadcasts such as those sent with Context.sendBroadcast
这个函数是用来改变当前广播传来的Extra额外信息的;它只能通过Context.sendOrderedBroadcast
.发送过来的广播有效;它使用Bundle来传递任意的数据,而这些数据只有接收器(broadcaster)才能解析。当然也可以把它设置为NULL,这样,它就把传来的数据映射全部清空了。
参数:
extras:新的数据映射,可以为空。
从上面的优先级可以看出,这里三个类的接收顺序是这样的:FirstRecever-》SecondRecever-》MyReceiver
下面看看FirstRecever如何利用setResultExtras来改变传来的Msg信息:
- public class FirstRecever extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- //先获得传过来的MSG
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, "FirstRecever:"+msg);
- //更改广播数据
- Bundle bundle = new Bundle();
- bundle.putString("msg", msg + "@FirstReceiver");
- setResultExtras(bundle);
- }
- }
最后利用setResultExtras(bundle); 修改当前的结果集。
如果想终止消息往下一个接收器传递,可以使用:abortBroadcast(); //终止消息再传递
这里有一个疑问:我利用setResultExtras(bundle); 修改传送结果,对原来广播过来的数据有影响吗?下面我们就在SecondRecever中做个测试
下面看看另外两个接收器代码:SecondRecever
- public class SecondRecever extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- //先获得广播过来的MSG
- String broadcast_msg = intent.getStringExtra("msg");
- Log.i(TAG, "SecondRecever--broadcast_msg:"+broadcast_msg);
- //接收通过setResultExtras传过来的msg
- String msg = getResultExtras(true).getString("msg");
- Log.i(TAG, "SecondReceiver: " + msg);
- //修改setResultExtras传来的结果
- Bundle bundle = new Bundle();
- bundle.putString("msg", msg + "@SecondReceiver");
- setResultExtras(bundle);
- }
- }
然后再利用getResultExtras(true).getString("msg"); 获得上一级传过来的setResultExtras(bundle); 里的数据;
最后重新将bundle里的数据中添加"@SecondReceiver"做个标记;
- public class MyReceiver extends BroadcastReceiver {
- private static final String TAG = "MyReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String msg = getResultExtras(true).getString("msg");
- Log.i(TAG, "MyReceiver: " + msg);
- }
- }
到这就所有就序了,运行下代码:
从结果可以看出:通过setResultExtras(bundle); 传递的数据是不会更改原生广播的数据的。也只是原来广播数据中额外添加的数据。
有序广播(添加访问权限版)
前面我们看到在sendOrderedBroadcast(intent, null); 中,第二个参数可以设定访问权限,在上个例子中,我们并没有加入访问权限,下面我们就发送一个带权限的广播:
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button btn= (Button)findViewById(R.id.sent_btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- send();
- }
- });
- }
- public void send() {
- Intent intent = new Intent("android.intent.action.MY_BROADCAST");
- intent.putExtra("msg", "hello receiver.");
- sendOrderedBroadcast(intent, "harvic.broadcast.perssion");
- }
- }
然后我们要在接收器中加入声明使用权限的代码:
有关权限的声明与使用,可以参考这篇文章:《声明、使用与自定义权限》
首先创建一个"harvic.broadcast.perssion"权限
- <permission android:name="harvic.broadcast.perssion" android:protectionLevel="normal"></permission>
- <uses-permission android:name="harvic.broadcast.perssion"/>
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.testbroadcast_order_perssion"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="14" />
- <permission android:name="harvic.broadcast.perssion" android:protectionLevel="normal"></permission>
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <receiver android:name=".FirstRecever" >
- <intent-filter android:priority="10">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".SecondRecever" >
- <intent-filter android:priority="9">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".MyReceiver" >
- <intent-filter android:priority="8">
- <action android:name="android.intent.action.MY_BROADCAST"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <!-- 接收优先级逐级降低 -->
- </application>
- <!-- 如果不添加使用权限声明,那么接收器会拒绝接受消息的,所以在Log中不会有任何显示 -->
- <uses-permission android:name="harvic.broadcast.perssion"/>
- </manifest>
有个地方要注意:即便像我们现在这样,自己的应用发出广播给自己接收,但如果不声明使用权限,是不能接收到广播的。这点与Activity的权限机制不一样,在Activity中,只要在同一个应用中相互App跳转,是不需要声明使用权限的,权限的限制只针对其它应用调用此Activity。
好啦,这篇文章到这就结束了。本篇内容比较多,涉及到的代码工程也比较多,现将相关源码列表如下:
1、静态注册源码
2、动态注册源码
3、普通接收源码
4、有序广播(无访问权限)源码
5、有序广播(添加访问权限)源码
6、本文所用图片
注意:在OnReceive中保存传过来值的问题:
如果有下面一段伪代码:
class xxxx{
private int mData=1;
public void onReceive(Context context, Intent intent)
Log.d("tag",mData + "");
mData = intent.getIntExtra("intdata",-1);
}
如果我们通过intent传过来的值保存在mData中,在下次再来的时候先打出来mData的值,会发现,每次打出来的都是1,所以根本没有办法通过成员变量保存onReceive中传过来的值。
源码下载地址:http://download.csdn.net/detail/harvic880925/7797869
请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/38710901 谢谢!
作者: 一点点征服
出处:http://www.cnblogs.com/ldq2016/
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利