Android基础总结三:BroadcastReceiver总结一
来源:互联网 发布:淘宝首页分类导航代码 编辑:程序博客网 时间:2024/06/05 02:43
BroadCastReceiver 简介
1.定义
- BroadCastReceiver(广播接收者) 是Android四大组件之一,与广播发送一起,利用intent机制,用于系统内的信息传递。
- 源码位于:framework/base/core/java/android.content.BroadcastReceiver.java
2.作用
- 用于监听/接收系统或应用内发出的广播信息,并做出响应
- 应用场景
a. 不同组件之间的通信(包括应用内/不同应用之间)
b.与Android系统在特定情况下的通信,如开机,电话呼入、网络可用等
c.多线程通信
3.实现原理
Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。
因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展
模型中有3个角色:
1.消息订阅者(广播接收者)
2.消息发布者(广播发布者)
3.消息中心(AMS,即Activity Manager Service)
原理描述
1.广播接收者通过Binder机制在AMS注册
2.广播发送者通过Binder机制向AMS发送广播
3.AMS根据广播发送者的要求,在已注册列表中,找到合适的广播接收者寻找依据:IntentFilter / Permission
4.AMS将广播发送到合适的广告接收者相应的消息循环排队中
5.广播接收者通过消息循环拿到次广告,并回调onReceive()
特别注意:广播发送者 和 广播接收者的执行 是 异步 的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;
BroadCastReceiver 使用
1 自定义广播接收者BroadcastReceiver
1.继承BroadcastReceiver类
2.重写抽象方法onReceive()方法
默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作(不能超过10秒钟),否则将导致ANR。
例子:
public class mBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //写入接收广播后的操作 } }
2 注册BroadcastReceiver
注册有两种方式:静态注册、动态注册
2.1 静态注册
在AndroidManifest.xml里通过标签声明:
<receiver android:enabled=["true" | "false"] <!--此broadcastReceiver能否接收其他App的发出的广播 默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false --> android:exported=["true" | "false"] android:icon="drawable resource" android:label="string resource" //继承BroadcastReceiver子类的类名 android:name=".mBroadcastReceiver" //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收; android:permission="string" //BroadcastReceiver运行所处的进程 //默认为app的进程,可以指定独立的进程 //注:Android四大基本组件都可以通过此属性指定自己的独立进程 android:process="string" > //用于指定此广播接收器将接收的广播类型 //本示例中给出的是用于接收网络状态改变时发出的广播 <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
当此App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
2.2 动态注册####
在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver,具体代码如下:
@Override protected void onResume() { super.onResume(); //实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); //设置接收广播的类型 intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE); //调用Context的registerReceiver()方法进行动态注册 registerReceiver(mBroadcastReceiver, intentFilter); } //注册广播后,要在相应位置记得销毁广播 //即在onPause() 中unregisterReceiver(mBroadcastReceiver) // 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中 // 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。 @Override protected void onPause() { super.onPause(); //销毁在onResume()方法中的广播 unregisterReceiver(mBroadcastReceiver); }
特别注意
动态广播最好在Activity的onResume()注册、onPause()注销。
原因:
1. 对于动态广播,有注册就必然得有注销,否则会导致内存泄露
重复注册、重复注销也不允许
2. Activity生命周期如下:
Activity生命周期的方法是成对出现的:
- onCreate() & onDestory()
- onStart() & onStop()
- onResume() & onPause()
在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。
不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:
- 当系统因为内存不足(优先级更高的应用需要内存,请看上图红框)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。
- 假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。
- 但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。
2.3 两种注册方式的区别
3 广播发送者向AMS发送广播###
发送方法####
广播使用sendBroadcast(intent)或者sendOrderedBroadcast(intent)发送:
Intent intent= new Intent();intent.setAction("android.intent.action.HIDE_STATUSBAR");sendBroadcast(intent);
广播类型####
1.普通广播(Normal Broadcast)
2.系统广播(System Broadcast)
3.有序广播(Ordered Broadcast)
4.粘性广播(Sticky Broadcast)
5.App应用内广播(Local Broadcast)
具体说明如下:
1. 普通广播(Normal Broadcast)
即开发者自身定义intent的广播(最常用)。发送广播使用如下:
Intent intent = new Intent();//对应BroadcastReceiver中intentFilter的actionintent.setAction(BROADCAST_ACTION);//发送广播sendBroadcast(intent);
若被注册了的广播接收者中注册时intentFilter的action与上述匹配,则会接收此广播(即进行回调onReceive())。如下mBroadcastReceiver则会接收上述广播:
<receiver //此广播接收者类是mBroadcastReceiver android:name=".mBroadcastReceiver" > //用于接收网络状态改变时发出的广播 <intent-filter> <action android:name="BROADCAST_ACTION" /> </intent-filter></receiver>
若发送广播有相应权限,那么广播接收者也需要相应权限
2. 系统广播(System Broadcast)
- Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
- 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
注意:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播
3. 有序广播(Ordered Broadcast)
定义:
- 发送出去的广播被广播接收者按照先后顺序接收
- 有序是针对广播接收者而言的
特点:
- 该广播的级别有级别之分,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;
- 同级别接收是先后是随机的,再到级别低的收到广播;
- 实验现象,在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先
- 先接收的广播接收者可以对广播进行截断(abortBroadcast()),即后接收的广播接收者不再接收到此广播;
- 先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播
例子:
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); }}
具体使用:
AndroidManifest 注册:
<?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" > …… <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > …… <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>…… </application></manifest>
发送广播:
Intent intent = new Intent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg", "hello receiver."); sendOrderedBroadcast(intent,null);
有序广播的发送主要是
使用:
添加访问权限:
sendOrderedBroadcast(intent, permission)中的第二个参数可以设定有序广播的访问权限:
Intent intent = new Intent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg", "hello receiver."); sendOrderedBroadcast(intent, "harvic.broadcast.permission");
这段代码中,我们利用 sendOrderedBroadcast(intent, “harvic.broadcast.perssion”); 发送一个必须拥有”harvic.broadcast.perssion”权限的接收器才能接收到我们的广播;
然后我们要在接收器中加入声明使用权限的代码:
<permission android:name="harvic.broadcast.permission" android:protectionLevel="normal"></permission>
然后是底部声明,我们要使用这个权限:
<uses-permission android:name="harvic.broadcast.permission"/>
所以总体的代码如下:
<?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" > …… <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> …… </application> <!-- 如果不添加使用权限声明,那么接收器会拒绝接受消息的,所以在Log中不会有任何显示 --> <uses-permission android:name="harvic.broadcast.perssion"/></manifest>
4. App应用内广播(Local Broadcast)
背景:
Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
冲突:
可能出现的问题:
其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息;
即会出现安全性 & 效率性的问题。
解决方案:
使用App应用内广播(Local Broadcast)
1.App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
2.相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高
具体使用1 – 将全局广播设置成局部广播:
1.注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,增设相应权限permission,用于权限验证;
3.发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
通过intent.setPackage(packageName)指定报名
具体使用2 – 使用封装好的LocalBroadcastManager类:
使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例
注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册
//注册应用内广播接收器//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); //步骤2:实例化LocalBroadcastManager的实例localBroadcastManager = LocalBroadcastManager.getInstance(this);//步骤3:设置接收广播的类型 intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);//取消注册应用内广播接收器localBroadcastManager.unregisterReceiver(mBroadcastReceiver);//发送应用内广播Intent intent = new Intent();intent.setAction(BROADCAST_ACTION);localBroadcastManager.sendBroadcast(intent);
5. 粘性广播(Sticky Broadcast)
由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。
特别注意
对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的
对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext
对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context
对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context
- 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
- Android基础总结三:BroadcastReceiver总结一
- Android四大组件之三:BroadcastReceiver总结
- Android基础总结之五:BroadcastReceiver
- BroadcastReceiver基础总结
- Android基础总结四:BroadcastReceiver总结二(广播权限设置)
- android中BroadCastReceiver总结
- Android BroadcastReceiver总结
- Android BroadcastReceiver学习总结
- Android BroadcastReceiver 广播总结
- Study-android BroadcastReceiver总结
- android BroadcastReceiver 总结
- Android BroadcastReceiver学习总结
- Android总结 - BroadcastReceiver
- Android BroadcastReceiver知识点总结
- Android之BroadcastReceiver总结
- BroadcastReceiver广播接收者基础总结
- android基础总结篇之五:BroadcastReceiver应用详解
- Android 基础总结:(七)BroadcastReceiver详解(上)
- JavaScript
- jQuery-(2)语法
- MySQL联合索引最左原则
- 神奇的Redis延迟
- 【读书精华分享】《分布式实时处理系统 原理、架构与实现》卢誉声著/2016年
- Android基础总结三:BroadcastReceiver总结一
- SPSS工具使用
- excel算出日期所属季度
- mysql 修复表 优化表
- python 字典和json树形输出
- 自定义alertView
- 用户注册,判断字符串个数,计算周数
- 如何理解 Hibernate的延迟加载机制?延迟加载与Session关闭的矛盾如何处理?
- 机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理)