Android应用程序可以从Android系统和其他Android应用程序发送或接收广播消息,类似于发布-订阅设计模式。Android中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容。Android广播机制,本质上它就是一种组件间的通信方式。广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
广播分为标准广播和有序广播
标准广播
这是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。接收器不能对收到的广播做任何处理,也不能截断广播继续传播。该种类的广播用sendBroadcast
发送。
1.发送广播
Intent intent = new Intent("com.czb.broadcast_test.MY_BROADCAST");
sendBroadcast(Intent);
2.想要接收自己发的广播也很简单:
新建一个BroadcastReceiver,
继承BroadcastReceiver
并重写onReceive(Context var1, Intent)
在Manifest里面加
表明想接收的广播的值
然后intent.getAction();
就可以接收到广播的值了,intent.getExtra("msg");
接收Intent里面存的数据。
有序广播
这是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。
该种类的广播用sendOrderedBroadcast
发送。
1.发送有序广播
Intent intent = new Intent("com.czb.broadcast_test.MY_BROADCAST");
sendOrderedBroadcast(Intent,null);
2.接收有序广播
1.可以识别BroadcastReceiver
的优先级;
2.可以截断广播。
先在Manifest
里面加一句来设置优先级
priority 必须是整数,默认是0 , 范围是[-1000, 1000]
然后在标准广播的接收器改一下就可以接收并截断广播了;
在onReceive
里面加一句abortBroadcast();
就可以截断广播了
粘性广播
粘性广播的特点是Intent会一直保留到广播事件结束,而这种广播也没有所谓的10秒限制,10秒限制是指普通的广播如果onReceive方法执行时间太长,超过10秒的时候系统会将这个广播置为可以被干掉的‘候选人’,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。该广播用sendStickyBroadcast
发送。
在Android5.0 & API 21中已经失效,所以不建议使用。
系统广播
例如当系统切换到飞机模式时。系统广播被发送到所有订阅了该事件的应用程序。
广播消息本身被包装在一个Intent对象中,其action字符串标识发生的事件(例如android.intent.action.AIRPLANE_MODE)。意图还可能包括绑定到其额外字段中的附加信息。例如,飞机模式意图包含一个额外的布尔值,指示飞机模式是否打开。
应用内广播(本地广播)
本地广播和其他的广播有些不同,它是使用LocalBroadcastManager
来发送广播以及注册广播接收器的。
优点:它发出的广播只会在应用程序的内部传播,不用担心广播被其他应用接收,造成数据泄漏,而广播接收器也只能接收到自己应用发出的广播,不会接收别的应用发来的广播,防止接收垃圾信息。
1.发送广播
LocalBroadcastManager lbm = LoadBroadcastManager.getInstance(this);
Intent intent = new Intent("com.czb.broadcast_test.MY_BROADCAST")
lbm.sendBroadcast(intent);
2.接收本地广播:
LocalBroadcastManager lbm = LoadBroadcastManager.getInstance(this);
...
LocalReceiver lr = new LocalReceive();
Intentfilter intentfliter = new Intentfliter("com.czb.broadcast_test.MY_BROADCAST");
//intentFilter.addAction("com.czb.broadcast_test.MY_BROADCAST");
lbm.registerReceiver(lr, intentfliter);//注册本地广播监听器
class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// unregisterReceiver(networkChangeReceiver);
lbm.unregisterReceiver(lr);
}
注意事项:
本地广播接收器的优势:
1.明确知道正在发送的广播不会离开我们的程序,因此不必担心信息泄露;
2.其他程序也没办法给我们的程序发送广播,没有安全隐患;
3.本地广播比一开始说的两种广播(系统全局广播)更加高效。
要想接受广播,我们需要在项目中注册广播接收者,而注册的方式有两种。
静态注册
一般为常驻广播,在AndroidManifest.xml里通过
intent过滤器里指定的是接收器订阅的action。
Android 8.0版本以后,系统不再支持,以下摘自官网的更新日志:
为提高设备性能,系统会限制未在前台运行的应用的某些行为。具体而言:
1. 现在,在后台运行的应用对后台服务的访问受到限制。
2. 应用无法使用其清单注册的大部分隐式广播(即,并非专门针对此应用的广播)。
动态注册
非常驻广播,在使用时注册,用完及时销毁。
BroadcastReceiver br = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);
记得及时注销,以免内存泄漏。
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
发广播
当调用sendBroadcast(Intent, String)
或sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
时,可以指定权限参数。只有申请了该权限的接收者才能接收广播。
//发广播
sendBroadcast(new Intent("com.example.NOTIFY"),
Manifest.permission.SEND_SMS);
要想接收这个广播,那么这个接收的app必须申请该权限
当然我们也可以自定义权限(
接收广播
同理,如果在注册广播接收器时指定了权限参数(使用registerReceiver(BroadcastReceiver,IntentFilter,String,Handler
)或清单文件中的
里),则只有在清单文件中使用
请求权限的广播发送者才可以将Intent发送给接收者。
比如,在清单文件中声明:
或者在注册的时候声明:
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
那么,如果要给它发消息,那广播发送的app就必须得申请获得相应的权限才行:
BroadcastReceiver#onReceive()
中执行耗时操作,如果有耗时工作,应该创建 IntentService
完成,而不应该在 BroadcastReceiver
内创建子线程去做。 说明:
由于该方法是在主线程执行,如果执行耗时操作会导致 UI 不流畅。可以使用IntentService
、创建 HandlerThread
或者调用Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
方法等方式,在其他Wroker线程执行 onReceive
方法。BroadcastReceiver#onReceive()
方法耗时超过 10 秒钟,可能会被系统杀死。
示例
IntentFilter filter = new IntentFilter();
filter.addAction(LOGIN_SUCCESS);
this.registerReceiver(mBroadcastReceiver, filter);
mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Intent userHomeIntent = new Intent();
userHomeIntent.setClass(this, UseHomeActivity.class);
this.startActivity(userHomeIntent);
}
};
避免使用隐式 Intent
广播敏感信息,信息可能被其他注册了对应BroadcastReceiver
的 App 接收。
说明:
通过Context#sendBroadcast()
发送的隐式广播会被所有感兴趣的receiver
接收,恶意应用注册监听该广播的receiver
可能会获取到Intent
中传递的敏感信息,并进行其他危险操作。如果发送的广播为使用Context#sendOrderedBroadcast()
方法发送的有序广播,优先级较高的恶意receiver
可能直接丢弃该广播,造成服务不可用,或者向广播结果塞入恶意数据。
如果广播仅限于应用内,则可以使用LocalBroadcastManager#sendBroadcast()
实现,避免敏感信息外泄和Intent
拦截的风险
Activity
或者Fragment
中动态注册BroadCastReceiver
时,registerReceiver()
和unregisterReceiver()
要成对出现。
说明
如果registerReceiver()
和unregisterReceiver()
不成对出现,则可能导致已经注册的receiver
没有在合适的时机注销,导致内存泄漏,占用内存空间,加重 SystemService
负担。
部分华为的机型会对receiver
进行资源管控,单个应用注册过多receiver
会触发管控模块抛出异常,应用直接崩溃。
广播的action
是全局的,所以命名的时候尽量包含app的命名空间,以免无意间和其他app冲突
尽量不要在广播接收者中启动activity
,这样用户体验很不好,可以考虑用发送通知的方式来代替。
两者及其接收广播的区别:
(1)动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期。注意在Activity结束前,移除广播接收器。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
(2)当广播为有序广播时:优先级高的先接收(不分静态和动态)。同优先级的广播接收器,动态优先于静态
(3)同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
(4)当广播为默认广播时:无视优先级,动态广播接收器优先于静态广播接收器。同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后册的。
https://www.jianshu.com/p/9eeff057cd52
https://www.jianshu.com/p/fae48283cc1c
https://blog.csdn.net/panhouye/article/details/53588930
https://blog.csdn.net/whdAlive/article/details/80250261