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系统 点击强行停止以后仍然起作用

相同点: 如果进程被系统杀掉 广播接收者 在广播到来的时候依然起作用 会重启进程

更多相关文章

  1. 浅谈:Android(安卓)TextView的append方法与滚动条同时使用
  2. Android(安卓)intent消息通知机制
  3. Android(安卓)之ActivityThead、ActivityManagerService 与activ
  4. Android(安卓)自定义消息右上角的数字提示或红点(类似微信或QQ的
  5. android 7.0 加入 android:directBootAware
  6. Android(安卓)Handler 深入学习(1)
  7. Androd开发艺术探索 第10章 Android的消息机制 读书笔记
  8. Android(安卓)-- Looper.prepare()和Looper.loop() —深入版
  9. Android(安卓)-- handler消息传送机制

随机推荐

  1. 太极图案例
  2. 模仿php.cn首页
  3. 模糊的身份證怎么P清楚清晰
  4. P身份證怎么在A4纸上打印
  5. CSS:圣杯布局例子-复习
  6. CSS:盒模型-复习
  7. WSL安装及配置图形界面
  8. 事件绑定、组件、路由实例演示
  9. 【精华】Android应用程序框架分析
  10. Android(安卓)studio使用Lottie- 让Andro