zoukankan      html  css  js  c++  java
  • Android BrocastReceiver解析

    简介

    BroadcastReceiver(广播接收器)是Android四大组件之一,是一个用来响应系统范围内的广播组件,可以从Android系统和其它app发送或接收广播消息,类似于发布 - 订阅设计模式。其特点是异步的,广播发送者不会关心有无接收者接收。可应用于不同组件之间的通信、多线程通信和系统在特定情况下的通信。

    原理

    图片
    对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同。但总体流程大致如下:

    1. 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
    2. 广播发送者通过Binder机制向AMS发送广播;
    3. AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
    4. 消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

    类型

    • 普通广播(Normal Broadcast)

    普通广播是完全异步的,通过Context的sendBroadcast()方法来发送,消息传递效率比较高,但所有receivers(接收器)的执行顺序不确定。缺点是接收器不能将处理结果传递给下一个接收器,并且无法在中途终止广播。

    val intent = Intent()
    intent.action = RECEIVE_TOKEN
    sendBroadcast(intent)
    
    • 系统广播(System Broadcast)

    Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开机启动,充电与电量变化,网络状态改变,拍照,屏幕关闭与开启等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。

    • 有序广播(Ordered Broadcast)

    “有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收,通过receiver的intent-filter中的android:priority属性来设置优先级,优先级从-1000~1000,数越大,优先级越高;priority属性相同者,动态注册的广播优先。其使用过程与普通广播非常类似,差异仅在于广播的发送方式通过Context.sendOrderedBroadcast()方法发送。

    • App应用内广播(Local Broadcast)

    Android中的广播可以跨App直接通信,可能会带来消耗性能和容易引起安全性的问题,为了解决这些问题,将全局广播设置成局部广播或者使用封装好的LocalBroadcastManager(只能动态注册)类。
    设置局部广播方式:

    • 注册广播时将exported属性设置为false
    • 增设相应权限permission,用于权限验证
    • 指定该广播接收器所在的包名

    LocalBroadcastManager使用

    //注册广播,在Activity.onResume注册
    val intentFilter = IntentFilter()
    val broadcastReceiver = BroadcastReceiver()
    intentFilter.addAction(RECEIVE_TOKEN)
    LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter)
    //发送广播
    val intent = Intent()
    intent.action = RECEIVE_TOKEN
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
    //取消广播,在Activity.onPause取消
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver)
    

    注册

    • 静态注册

    直接在Manifest.xml文件的节点中配置,使用< receiver >标签声明,并在标签内用 < intent-filter > 标签设置过滤器,该注册方式不管app是否处于活动状态,都会进行监听。

    <receiver android:name=".BroadcastReceiver" // 继承BroadcastReceiver子类的类名
              android:enabled="boolean"  // 能否接收其他App的发出的广播
              android:exported="boolean" // 默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
              android:icon="drawable resource" // 广播icon
              android:label="string resource" // 广播标签
              android:permission="string" // 具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收
              android:process="string"> // 指定自己的独立进程
        <intent-filter>
            <action android:name="com.fomin.demo.ACTION_RECEIVE_TOKEN"/>
        </intent-filter>
    </receiver>
    

    但需要注意的是,Android8.0系统对静态广播做了变更,具体可查看[https://blog.csdn.net/fomin_zhu/article/details/84454042]

    • 动态注册

    直接在代码在代码中调用Context.registerReceiver()方法注册和调用unregisterReceiver
    取消注册

    override fun onResume() {
        super.onResume()
        //注册广播,在Activity.onResume注册
        val intentFilter = IntentFilter()
        intentFilter.addAction(RECEIVE_TOKEN)
         LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter)
    }
    
    override fun onPause() {
        super.onPause()
        //取消广播,在Activity.onPause取消
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver)
    }
    

    动态广播最好在Activity 的 onResume()注册、onPause()注销,是因为对于动态广播,有注册就必然得有注销,否则会导致内存泄露;在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。

    • 两者区别
      1. 静态注册在Manifest.xml注册,动态在代码上调用
      2. 静态是常驻的,不受生命周期影响,并且耗电和占内存;而动态是使用才注册,跟随组件生命周期

    注意

    不同注册方式的广播接收器回调OnReceive()中的context返回值是不一样的

    • 静态注册广播,content是android.app.ReceiverRestrictedContext,此context非Activity类型,不可直接用来构造AlertDialog
    • 动态注册广播,传入onReceive()方法里的Context对象context其实就是调用sendBroadcast()的Activty对象;但LocalBroadcastManager方式返回的是Application Context
  • 相关阅读:
    hadoop节点的增删
    hadoop集群搭建
    主从配置
    CentOS7ssh互信
    Java根据视频的URL地址,获取视频时长
    Mybatis-plus使用@Select注解使用IN查询不出数据的问题
    洗牌算法
    1525
    SpringBoot+Quartz+MySQL实现分布式定时任务
    微信小程序授权登录解密失败问题
  • 原文地址:https://www.cnblogs.com/fomin/p/10490848.html
Copyright © 2011-2022 走看看