一 概述

Broadcast作为android中的四大组件,其重要性可想而知, 在android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制;这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用。android广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。然而,广播机制却是不可替代的,它和Binder机制不一样的地方在于,广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。

下面是广播的特性:

1.广播接收者的生命周期是非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁
2.广播接收者中不要做一些耗时的工作,否则会弹出Application No Response错误对话框
3.最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉
4.耗时的较长的工作最好放在服务中完成

说到Broadcast,那还必须说下BroadcastReceiver,从单词意思可以看出是个广播接受器,对它的作用就是接受广播的一个组件,BroadcastReceiver是一种可以让用户接受广播通知且没有用户界面的组件,在android系统中为了响应不同的事件通知,应用程序可以注册不同的BroadcastReceiver,当BroadcastReceiver接受到通知后可以启动Activity作为响应或者通过NotificationManager提醒用户。

Broadcast也有四种不同的分类:

  • 普通广播(Normal Broadcast)
  • 系统广播(System Broadcast)
  • 有序广播(Ordered Broadcast)
  • 粘性广播(Sticky Broadcast)
  • App应用内广播(Local Broadcast)

二 普通广播(Normal Broadcast)

这种广播所有监听该广播的接收器都可以监听到该广播,同一个级别的接受顺序是随机的或则说是无序的,接收器不能对收到的广播做任何处理,也不能截断广播继续传播。该种类的广播用sendBroadcast发送。下面做个简单的例子: 1.自定义一个广播接收器MyBroadcaseReceiver,集成BroadcastReceiver抽象类,并实现onReceive方法,该方法是接受到广播做的事情,在这个例子中当收到广播是Toast信息。
package com.example.unorderbroadcasereceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * @author ZMC * */public class MyBroadcaseReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubToast.makeText(context, "Normal broadcast", Toast.LENGTH_SHORT).show();}}
作为四大组件,还要记得在AndroidManifest中注册:
                                                
2.布局文件
    
这里简单的定义一个按钮,点击发送广播 3. MainActivity.java
package com.example.unorderbroadcasereceiver;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;/** * @author ZMC * */public class MainActivity extends Activity {private MyBroadcaseReceiver myBroadcaseReceiver;private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myBroadcaseReceiver = new MyBroadcaseReceiver();//方法一:动态注册//方法二:在Androidmanifest.xml中注册IntentFilter filter = new IntentFilter();filter.addAction("unorderbroadcast");registerReceiver(myBroadcaseReceiver, filter );//按鈕button = (Button)findViewById(R.id.btn);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent intent = new Intent();intent.setAction("unorderbroadcast");sendBroadcast(intent);}});}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();unregisterReceiver(myBroadcaseReceiver);}}
上述代码中也运用了动态注册MyBroadcaseReceiver,如果使用该方法,就不用在AndroidManifest中另外注册了,但是使用该方法,要记得在onDestory方法中记得unregisterReceiver,要不然在程序退出的时候会报如下错误:

4 结果


当点击按钮发送广播后,因为BroadcastReceiver一直在后台待命接收,当收到action为unorderbroadcast的广播是,就做出响应。这里就简单的Toast。

三 系统广播(System Broadcast)

android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广波,每个广播都有特定的Intent - Filter(包括具体的action),android常用系统广播action如下:

Operation action
监听网络变化 android.net.conn.CONNECTIVITY_CHANGE
关闭或打开飞行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED
充电时或电量发生变化 Intent.ACTION_BATTERY_CHANGED
电池电量低 Intent.ACTION_BATTERY_LOW
电池电量充足(即从电量低变化到饱满时会发出广播 Intent.ACTION_BATTERY_OKAY
系统启动完成后(仅广播一次) Intent.ACTION_BOOT_COMPLETED
按下照相时的拍照按键(硬件按键)时 Intent.ACTION_CAMERA_BUTTON
屏幕锁屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
设备当前设置被改变时(界面语言、设备方向等) Intent.ACTION_CONFIGURATION_CHANGED
插入耳机时 Intent.ACTION_HEADSET_PLUG
未正确移除SD卡但已取出来时(正确移除方法:设置–SD卡和设备内存–卸载SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL
插入外部储存装置(如SD卡) Intent.ACTION_MEDIA_CHECKING
成功安装APK Intent.ACTION_PACKAGE_ADDED
成功删除APK Intent.ACTION_PACKAGE_REMOVED
重启设备 Intent.ACTION_REBOOT
屏幕被关闭 Intent.ACTION_SCREEN_OFF
屏幕被打开 Intent.ACTION_SCREEN_ON
关闭系统时 Intent.ACTION_SHUTDOWN
重启设备 Intent.ACTION_REBOOT
因为系统广播比较简单这里就不写例子了,当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播。

四 有序广播(Ordered Broadcast)

android中的有序广播,也是一种比较常用的广播,该种类的广播用sendOrderedBroadcast发送。,该中广播主要有一下特性:
  1. 按照接收者的优先顺序来接收广播,优先级别在intent-filter中的priority中声明,-10001000之间,值越大优先级越高,
  2. 可以终止广播的继续传播,接受者可以修改intent的内容。
  3. 同级别接收顺序是随机的,级别低的后收到
  4. 能截断广播的继续传播,高级别的广播接收器接收广播后能决定时候截断。能处理广播
  5. 同级别动态注册高于静态注册

有序广播例子: 1.自定义两个BroadcastReceiver MyBroadcastReceiver.java
package com.example.orderbroadcastreceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.widget.Toast;public class MyBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubString data = intent.getStringExtra("data");Toast.makeText(context, data, Toast.LENGTH_SHORT).show();Bundle bundle = new Bundle();bundle.putString("data", "from MyBroadcastReceiver");setResultExtras(bundle );}}
SecondBroadcastReceiver.java
package com.example.orderbroadcastreceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.widget.Toast;public class SecondBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubBundle bundle = getResultExtras(false);String data = bundle.getString("data");Toast.makeText(context, data, Toast.LENGTH_SHORT).show();}}
2.布局文件 这里也是简单的定义一个按钮发送有序广播
    
3.MainActivity.java
package com.example.orderbroadcastreceiver;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 MyBroadcastReceiver myBroadcastReceiver;private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = (Button)findViewById(R.id.btn);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent intent = new Intent();intent.putExtra("data", "from activity");intent.setAction("orderbroadcast");sendOrderedBroadcast(intent, null);}});}}
4.AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>                                                                                                                                                                                                    
在这个例子中直接使用了静态注册的方式,并分别为两个Receiver设置了优先级。 4.结果

当程序之后,有MyBroadcastReceiver和SecondBroadcastReceiver在后台待命接收action为orderbroadcast的广播,点击发送按钮之后这两个广播就能先后接收到了。因为MyBroadcastReceiver的优先级为1000,SecondBroadcastReceiver的优先级为500,所以MyBroadcastReceiver先收到广播,而SecondBroadcastReceiver后接收到广播,同时SecondBroadcastReceiver接收到的是MyBroadcastReceiver处理后的广播,因此它们两个接收到的消息不一样。另外,优先级高的Receiver可以对广播进行截断,截断后比它优先级低的Receiver就不能接收到该广播了,若想截断广播,就只需在Receiver中的onReceive方法中执行abortBroadcast()方法即可。

五 粘性广播(Sticky Broadcast)

粘性广播的特点是Intent会一直保留到广播事件结束,而这种广播也没有所谓的10秒限制,10秒限制是指普通的广播如果onReceive方法执行时间太长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。该广播用sendStickyBroadcast发送。
粘性广播例子: 1.自定义MyBroadcastReceiver MyBroadcastReceiver.java
package com.example.stickybroadcastdemo;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;public class MyBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubToast.makeText(context, "stickybroadcast", Toast.LENGTH_SHORT).show();}}
2.布局文件 定义两个按钮,一个按钮发送粘性广播,一个按钮注册Receiver
    
3.MainActivity.java
package com.example.stickybroadcastdemo;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;/** * @author ZMC * */public class MainActivity extends Activity {private Button button,button1;MyBroadcastReceiver myBroadcastReceiver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = (Button)findViewById(R.id.btn);button1 = (Button)findViewById(R.id.btn1);//发送广播button.setOnClickListener(new OnClickListener() {@SuppressWarnings("deprecation")@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent intent = new Intent("stickybroadcast");sendStickyBroadcast(intent);}});//点击注册Receiverbutton1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubreg(v);}});}//动态注册Receiverprivate void reg(View view){IntentFilter filter = new IntentFilter();filter.addAction("stickybroadcast");myBroadcastReceiver = new MyBroadcastReceiver();registerReceiver(myBroadcastReceiver, filter);}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();unregisterReceiver(myBroadcastReceiver);}}
上述代码中button1按钮动态注册Receiver,button发送粘性广播,同时特别注意: 发送粘性广播要使用这个api需要权限android.Manifest.permission.BROADCAST_STICKY,在AndroidManifest.xml中配置如下:
4.结果


当点击sendbroadcast按钮是,因为没有注册Receiver,所以没有响应,这时再点击registerReceiver按钮时,动态注册Receiver,这时就显示之前发送广播的消息。因此这种广播就是 Intent会一直保留到广播事件结束,即使Recceiver没有注册。

六 App应用内广播(Local Broadcast)

因为android中的广播是可以跨域的(跨App),因此可能存在一下问题:
  • 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
  • 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
对于上述情况就出现了这种广播,App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。相比于全局广播(普通广播),App应用内广播优势体现在:安全性高效率高。 因为要测试两个app,所以下面这个例子基于普通广播的那个项目,普通广播的那个项目发送广播,同时有两个app里都有一个action为unorderbroadcast的Receiver待命接收。 没有设置App应用广播的情况:

app test的代码

test app中只有一个MyBroadcaseReceiver来接受另一个app发送的广播: MyBroadcaseReceiver.java
package com.example.unorderbroadcasereceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * @author ZMC * */public class MyBroadcaseReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubToast.makeText(context, "App 2:Normal broadcast", Toast.LENGTH_SHORT).show();}}
同时注册Receiver
                                                

App 2的代码:

1.自定义一个广播接收器MyBroadcaseReceiver.
package com.example.unorderbroadcasereceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * @author ZMC * */public class MyBroadcaseReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubToast.makeText(context, "App 2:Normal broadcast", Toast.LENGTH_SHORT).show();}}
2.布局文件
    
这里简单的定义一个按钮,点击发送广播 3. MainActivity.java
package com.example.unorderbroadcasereceiver;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;/** * @author ZMC * */public class MainActivity extends Activity {private MyBroadcaseReceiver myBroadcaseReceiver;private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myBroadcaseReceiver = new MyBroadcaseReceiver();//方法一:动态注册IntentFilter filter = new IntentFilter();filter.addAction("unorderbroadcast");registerReceiver(myBroadcaseReceiver, filter );//按鈕button = (Button)findViewById(R.id.btn);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent intent = new Intent();intent.setAction("unorderbroadcast");sendBroadcast(intent);}});}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();unregisterReceiver(myBroadcaseReceiver);}}

4.结果:

以上没有设置广播为App内广播,以此另一个app也能接受到广播。
5.下面将App 2中的广播改为App应用内广播,只需修改MainActivity.java中的代码即可,改后的代码如下:
package com.example.unorderbroadcasereceiver;import android.app.Activity;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.support.v4.content.LocalBroadcastManager;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;/** * @author ZMC * */public class MainActivity extends Activity {private MyBroadcaseReceiver myBroadcaseReceiver;LocalBroadcastManager localBroadcastManager;private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myBroadcaseReceiver = new MyBroadcaseReceiver();//方法一:动态注册IntentFilter filter = new IntentFilter();filter.addAction("unorderbroadcast");//实例化LocalBroadcastManager的实例localBroadcastManager = LocalBroadcastManager.getInstance(this);//调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 localBroadcastManager.registerReceiver(myBroadcaseReceiver, filter);//按鈕button = (Button)findViewById(R.id.btn);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub//发送应用内广播Intent intent = new Intent();intent.setAction("unorderbroadcast");localBroadcastManager.sendBroadcast(intent);}});}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();//取消注册应用内广播接收器localBroadcastManager.unregisterReceiver(myBroadcaseReceiver);}}

需要注意的是对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册。 6.结果:
. 设置广播为App应用内广播,广播就不能跨域了,一次只能在发送广播的app(App2)内接受,而另外一个app(app test)接收不到app2发送的广播。

七 总结

android中的广播分为五种:普通广播、系统广播、有序广播、粘性广播、App应用内广播,不用的广播都有不同的使用场景。BroadcastReceiver有静态注册和动态注册两种方式,当用动态注册方式时要记得销毁的时候取消注册。

对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

  • 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
  • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
  • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。

更多相关文章

  1. 浅析Android——Android(安卓)8.0(O)后台服务的限制和变化
  2. Android系统分析之Activity的启动流程
  3. 带你从源代码详细分析View的绘制过程
  4. Android(安卓)源码本地编译脚本 & 编译Android系统
  5. Android线程学习总结
  6. 关于Android内存泄漏的种种总结第二弹
  7. Android面试题——Activity
  8. Android(安卓)LottieAnimation使用---踩坑篇
  9. 【Android(安卓)学习记录】Fragment注入漏洞

随机推荐

  1. android > TimePicker 时间设置控件
  2. android电话拦截及短信过滤
  3. android edittext 不自动获取焦点
  4. Android警告错误搜集
  5. Android实现拍照功能实例
  6. Android Button按钮两个页面切换
  7. Android中ContentProvider和Uri用法
  8. java android 环境变量配置备忘录
  9. Android: PowerManager.WakeLock
  10. android ListView 显示在底部