android四大组件之Broadcast(广播)使用详解
一 概述
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 |
四 有序广播(Ordered Broadcast)
android中的有序广播,也是一种比较常用的广播,该种类的广播用sendOrderedBroadcast发送。,该中广播主要有一下特性:按照接收者的优先顺序来接收广播,优先级别在intent-filter中的priority中声明,-
1000
到
1000
之间,值越大优先级越高,
- 可以终止广播的继续传播,接受者可以修改intent的内容。
- 同级别接收顺序是随机的,级别低的后收到
- 能截断广播的继续传播,高级别的广播接收器接收广播后能决定时候截断。能处理广播
- 同级别动态注册高于静态注册
有序广播例子: 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 test的代码
test app中只有一个MyBroadcaseReceiver来接受另一个app发送的广播: MyBroadcaseReceiver.javapackage 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。
更多相关文章
- 浅析Android——Android(安卓)8.0(O)后台服务的限制和变化
- Android系统分析之Activity的启动流程
- 带你从源代码详细分析View的绘制过程
- Android(安卓)源码本地编译脚本 & 编译Android系统
- Android线程学习总结
- 关于Android内存泄漏的种种总结第二弹
- Android面试题——Activity
- Android(安卓)LottieAnimation使用---踩坑篇
- 【Android(安卓)学习记录】Fragment注入漏洞