浅谈Android四大组件之BroadcastReceiver
1、介绍
Android中定件义了很多很多的事件如,电话打出去了,电量变化了,信号变化了,应用安装了,屏幕开启了关闭了,当这些事件到来的时候;android就会发送广播如果想到监听到这些事件,就需要注册广播接收者,主要为了方便开发者进行开发。
内部实现机制:
- 1、自定义广播接收者BroadcastReceiver,并复写onReceive()方法;
- 2、通过Binder机制向AMS(Activity Manager Service)进行注册;
- 3、广播发送者通过Binder机制向AMS发送广播;
- 4、AMS查找符合相应条件(IntentFilter / Permission等)的BrocastReceiver,将广播发送到BrocastReceiver
- (一般情况下为Activity)相应的消息循环队列中;
- 5、消息循环队列拿到此广播,回调BrocastReceiver中的onReceiver()方法。
2、简单案列-sd卡状态监听(系统广播)
广播接收者的用法
public class SdcradReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // // //一个广播接收者 可以同时注册多个广播的接受事件 //通过action去区分 //获取action 通过intent String action = intent.getAction(); if (action.equals("android.intent.action.MEDIA_MOUNTED")) { System.out.println("sd卡 安装成功"); } if (action.equals("android.intent.action.MEDIA_UNMOUNTED")) { System.out.println("sd卡 卸载了"); } Uri data = intent.getData(); System.out.println(data); } }
清单代码:
<receiver android:name="cn.test.sdcard.SdcradReceiver" > <intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED" /> <action android:name="android.intent.action.MEDIA_UNMOUNTED" /> <data android:scheme="file" /> intent-filter> receiver>
3、简单案列-无序广播(开发者自己手动发广播)
无序广播:相当于新闻联播7点钟准时开始放不能配置优先权,广播不能被中断,系统广播一般是无序广播
实现广播接收类:
public class NormalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String data = intent.getStringExtra("news"); Toast.makeText(context, data, 0).show(); }}
清单文件注册广播接收:
<receiver android:name="cn.test.receivenormal.NormalReceiver"> <intent-filter > <action android:name="cn.test.news"/> intent-filter> receiver>
发送广播:
Intent intent=new Intent(); //为了让广播接收者 有接收到的action intent.setAction("cn.test.news"); intent.putExtra("news", "新闻联播每天晚上7点准时与您相约,不见不散哦"); sendBroadcast(intent);
4、简单案列-有序广播
有序广播:红头文件 可以配置优先权.广播是可以中断的
背景介绍:
发送一个有序广播,有多个广播接收者,分别为 CityReceiver TownReceiver FarmerReceiver 以及最终的广播接收者ResultReceiver(返回有序广播的接受情况)
申明多个广播接受着:
public class CityReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String data = getResultData(); Toast.makeText(context, "市政府收到的信息:"+data, 0).show(); setResultData("皇上给灾民赈灾粮20万石"); }}
public class TownReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String data = getResultData(); Toast.makeText(context, "镇政府收到的信息:"+data, 0).show(); setResultData("皇上给灾民赈灾粮10万石"); }}
public class FarmerReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String data = getResultData(); Toast.makeText(context, "灾民收到的信息:" + data, 0).show(); }}
再定义这三个广播接收者的清单:
<receiver android:name="cn.test.receiveorder.CityReceiver"> //优先级 0-1000 <intent-filter android:priority="1000"> <action android:name="cn.daqing.rice"/> intent-filter> receiver><receiver android:name="cn.test.receiveorder.TownReceiver"> <intent-filter android:priority="666"> <action android:name="cn.daqing.rice"/> intent-filter> receiver><receiver android:name="cn.test.receiveorder.FarmerReceiver"> <intent-filter android:priority="110"> <action android:name="cn.daqing.rice"/> intent-filter> receiver>
定义最终的广播接收者,自己的广播接收者,该接受者不需要在清单文件中配置,主要由于向发送广播者反应发送的广播最终情况,这个可以根据自己发送有序广播参数选择是否添加:
public class ResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String data = getResultData(); Toast.makeText(context, "报告皇上灾民收到信息是:"+data, 0).show(); }}
发送有序广播
Intent intent=new Intent(); intent.setAction("cn.daqing.rice"); ResultReceiver resultReceiver = new ResultReceiver(); //receiverPermission 接受的权限 //resultReceiver 结果接收者 最终的广播接收者 自己的广播接收者 不需要在清单文件中配置 //广播被优先级高的 中断 依然可以收到广播 最后收到广播 //scheduler handler //initialData 初始化的数据 //initialExtras 额外的数据 // sendOrderedBroadcast(Intent intent, // String receiverPermission, // BroadcastReceiver resultReceiver, // Handler scheduler, // int initialCode, // String initialData, // Bundle initialExtras) sendOrderedBroadcast(intent, null, resultReceiver, null, RESULT_OK, "皇上给灾民赈灾粮50万石", null);
注意:abortBroadcast(),进行广播拦截的时候 是不能拦截最终的广播(resultReceiver )。
- 优先级相同
不同应用 谁先在手机中安装 谁先接受到信息
同一应用 谁先在清单文件中注册 谁先接收到信息
- 优先级不同
不管应用是否相同 都是 按优先级的顺序接受广播
5、动态注册广播
以上广播注册都是在清单文件中进行注册(静态注册)的,而操作非常频繁广播,比如屏幕的关闭和开启电池电量的变化不能直接在清单文件中注册
,这时需要动态的在代码中注册一般在 onCreate()方法中注册,并把receiver声明成全局变量,最后在onDestory()中注销
即:
public class MainActivity extends Activity { private ScreenReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //代码 动态的注册广播接收者// /receiver 要注册 广播接收者 //filter 过滤器 receiver = new ScreenReceiver(); IntentFilter filter=new IntentFilter(); filter.addAction("android.intent.action.SCREEN_ON"); filter.addAction("android.intent.action.SCREEN_OFF"); registerReceiver(receiver, filter); } //当activity销毁的时候调用 @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); unregisterReceiver(receiver); }}
广播接收者:
public class ScreenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals("android.intent.action.SCREEN_ON")) { System.out.println("屏幕亮了"); } if (action.equals("android.intent.action.SCREEN_OFF")) { System.out.println("屏幕灭了"); } }}
6 LocalBroadcastManager
LocalBroadcastManager是Android Support包提供了一个工具,用于在同一个应用内的不同组件间发送Broadcast。LocalBroadcastManager也称为局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信,可以用来代替Handler更新UI
好处:
1、因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。 2、不用担心别的应用伪造广播,造成安全隐患。 3、相比在系统内发送全局广播,它更高效。
LocalBroadcastManager用法
1、LocalBroadcastManager对象的创建
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
2、注册广播接收器
LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );
3 、发送广播
LocalBroadcastManager.sendBroadcast( intent ) ;
4、取消注册广播接收器
LocalBroadcastManager.unregisterReceiver( broadcastReceiver );
- LocalBroadcastManager部分源码解析
private static LocalBroadcastManager mInstance; public static LocalBroadcastManager getInstance(Context context) { synchronized (mLock) { if (mInstance == null) { mInstance = new LocalBroadcastManager(context.getApplicationContext()); } return mInstance; } } private LocalBroadcastManager(Context context) { mAppContext = context; mHandler = new Handler(context.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_EXEC_PENDING_BROADCASTS: executePendingBroadcasts(); break; default: super.handleMessage(msg); } } }; }
从这个部分源码可以看出两点:
1、在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。
2、在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。
3、在创建Handler的时候,用了 context.getMainLooper() , 说明这个Handler是在Android 主线程中创建的,广播接收器的接收消息的时候会在Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。
- 案例:
public class MainActivity extends AppCompatActivity { private LocalBroadcastManager localBroadcastManager ; private MyBroadcastReceiver broadcastReceiver ; private IntentFilter intentFilter ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //注册广播接收器 localBroadcastManager = LocalBroadcastManager.getInstance( this ) ; broadcastReceiver = new MyBroadcastReceiver() ; intentFilter = new IntentFilter( "myaction") ; localBroadcastManager.registerReceiver( broadcastReceiver , intentFilter ); //在主线程发送广播 Intent intent = new Intent( "myaction" ) ; intent.putExtra( "data" , "主线程发过来的消息" ) ; localBroadcastManager.sendBroadcast( intent ) ; new Thread(new Runnable() { @Override public void run() { //在子线程发送广播 Intent intent = new Intent( "myaction" ) ; intent.putExtra( "data" , "子线程发过来的消息" ) ; localBroadcastManager.sendBroadcast( intent ) ; } }).start(); ; } private class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction() ; if ( "myaction".equals( action )){ Log.d( "tttt 消息:" + intent.getStringExtra( "data" ) , "线程: " + Thread.currentThread().getName() ) ; } } } @Override protected void onDestroy() { super.onDestroy(); //取消注册广播,防止内存泄漏 localBroadcastManager.unregisterReceiver( broadcastReceiver ); }}
运行结果
D/tttt 消息:主线程发过来的消息: 线程: main
D/tttt 消息:子线程发过来的消息: 线程: main
可以看出,广播接收器的onReceive方法运行在主线程。
注意事项
虽然LocalBroadcastManager也通过BroadcastReceiver来接收消息,但是他们两个之间还是有很多区别的。
- LocalBroadcastManager注册广播只能通过代码注册的方式。传统的广播可以通过代码和xml两种方式注册。
- LocalBroadcastManager注册广播后,一定要记得取消监听。这一步可以有效的解决内存泄漏的问题。
- LocalBroadcastManager注册的广播,您在发送广播的时候务必使用LocalBroadcastManager.sendBroadcast(intent); 否则您接收不到广播。传统的发送广播的方法:context.sendBroadcast( intent );
7、注意
原因:随着android版本的提高,android的安全性,隐私性不断增强。
- 4.0以上系统,在广播接收者第一安装的时候必须要有界面,没有界面,广播接收者不起作用,如果第一次安装有界面,第二次安装没有界面,起作用;
- 2.3系统,不管安装有没有界面 广播接收者都会起作用
- 4.0以上 如果 应用程序被 apps===>app 点击强行停止 广播接收者不在起作用
- 2.3系统 点击强行停止以后仍然起作用
相同点: 如果进程被系统杀掉 广播接收者 在广播到来的时候依然起作用 会重启进程
更多相关文章
- 浅谈:Android(安卓)TextView的append方法与滚动条同时使用
- Android(安卓)intent消息通知机制
- Android(安卓)之ActivityThead、ActivityManagerService 与activ
- Android(安卓)自定义消息右上角的数字提示或红点(类似微信或QQ的
- android 7.0 加入 android:directBootAware
- Android(安卓)Handler 深入学习(1)
- Androd开发艺术探索 第10章 Android的消息机制 读书笔记
- Android(安卓)-- Looper.prepare()和Looper.loop() —深入版
- Android(安卓)-- handler消息传送机制