【Android】四大组件之BroadcastReceiver

广播接收者

概念

BroadcastReceiver接收系统发出的广播,系统在运行过程中,会发生很多事件,系统为了让其他应用知道系统发生了这个事件,会发送一个对应该事件的广播,比如:电量改变,收到短信,拨打电话,屏幕解锁,系统开机。

原理

广播是通过intent发送的,intent中会携带一个action,当一条广播被发送出来时,系统是在所有清单文件中遍历,通过匹配意图过滤器找到能接收这条广播的广播接收者。

广播接收者所在进程即便没有启动,广播发送出来,系统也会启动这个进程,然后把广播交给广播接收者

定义方式

  • 定义一个类继承BroadcastReceiver
  • 在清单文件中进行配置,使用< intent-filter >标签下< action >标签指定接收对应广播的类型

广播接收者的特性

  • 系统应用的广播接收者优先级不会超过1000
  • 4.0之后,广播接收者所在的应用必须启动过一次,才能生效
  • 4.0之后,如果广播接收者所在应用被用户手动关闭了,那么再也不会启动了,直到用户再次手动启动该应用。如果是系统关闭的,还是可以启动的

广播接收者例子

IP拨号器

原理

系统拨打号码时,会发出一个广播,广播中携带拨打的号码,接收拨打电话的广播,修改广播内携带的电话号码,再发送出去

实现

  • 定义广播接收者接收打电话广播
  • 在清单文件中定义该广播接收者接收的广播类型,注册
<receiver android:name=".CallReceiver">
    <intent-filter>
        <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
    </intent-filter>
</receiver>
  • 接收打电话广播需要权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
  • 6.0以上的系统需要动态申请权限 Manifest.permission.PROCESS_OUTGOING_CALLS
 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.PROCESS_OUTGOING_CALLS),100)
  • 在定义的广播接收者中,获取拨打的号码,将号码进行修改,然后发送出去
class CallReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        //获取拨打的电话
        val data = resultData
        println("拨打的电话是:${data}")
        //发送修改后的电话
        resultData = "888${data}"
    }
}
  • 即使广播接收者的进程没有启动,当系统发送的广播可以被该接收者接收时,系统会自动启动该接收者所在的进程

短信拦截器

原理

系统负责接收短信,短信应用并不会接收短信,它接收广播。系统收到短信时会产生一条广播,广播中包含了短信的号码和内容。但短信广播发出时,使用广播接收者接收该广播,获取此短信的号码,与黑名单中的号码进行比对,如果存在,则拦截广播,短信应用中就不会接收到此广播了。高版本无法实现拦截,即使优先级很高,系统短信应用仍然可以接收到短信

实现

  • 定义接收短信的广播接收者
  • 在清单文件中注册该广播,监听短信的action已被系统隐藏了,在上层应用中是无法显式调用的,需要使用 android.provider.Telephony.SMS_RECEIVED,注册此广播时,需要设置优先级,要保证优先级高于系统短信,才可以实现拦截
<receiver android:name=".SmsReceiver">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
    </intent-filter>
</receiver>
  • 需要申请接收短信的权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
  • 6.0以上的系统需要动态申请接收短信的权限
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECEIVE_SMS),100)
  • 获取接收到的短信,系统创建广播时,把短信存放到一个数组,然后把数据以pdus为key存入bundle,再把bundle存入intent
class SmsReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        intent ?: return
        println("收到短信")
        val bundle = intent.extras
        bundle?.let {
            //如果短信太长,则会分成多条消息发送过来,所以是数组
            val objs = it.get("pdus") as Array<Any>
            objs.forEach {obj->
               val smsMessage= SmsMessage.createFromPdu(obj as ByteArray)
                val address = smsMessage.originatingAddress
                val messageBody = smsMessage.messageBody
                println("address:${address},messageBody:${messageBody}")
            }
        }
    }
}

SD卡监听

原理

监听sd卡的状态,对于下载文件到sd卡是非常有必要的,根据不同的状态,对于实际的应用场景,做出对应的逻辑处理

实现

  • 定义一个监听sd卡状态的广播接收者
  • 在清单文件中注册该广播接收者,监听的action是
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<!-- 还需要匹配一个scheme -->
<data android:scheme="file"/>
  • 不需要权限
  • 在广播接收者中分别处理不同的状态
class SdReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when( intent?.action){
            Intent.ACTION_MEDIA_MOUNTED->{
                Toast.makeText(context,"sd卡就绪",Toast.LENGTH_SHORT).show()
            }
            Intent.ACTION_MEDIA_UNMOUNTED->{
                Toast.makeText(context,"sd卡被卸载了",Toast.LENGTH_SHORT).show()
            }
            Intent.ACTION_MEDIA_REMOVED->{
                Toast.makeText(context,"sd卡被拨出了",Toast.LENGTH_SHORT).show()
            }
        }
    }
}

开机启动

原理

监听开机启动事件可以做一些事情,可以启动指定的应用。系统开机时,系统会发出开机广播

实现

  • 定义一个广播接收者,接收开机启动
  • 在清单文件中注册该广播,过滤action
<action android:name="android.intent.action.BOOT_COMPLETED"/>
  • 需要在清单文件中添加开机权限,无需动态申请权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        println("已开机")
    }
}

应用安装卸载

原理

应用在安装卸载更新时,系统会发送广播,广播里会携带应用的包名

实现

  • 定义一个广播接收者,接收应用的安装卸载和更新
  • (8.0以下)在清单文件中注册该广播,对应的action
<receiver android:name=".AppReceiver">
   <intent-filter>
       <action android:name="android.intent.action.PACKAGE_ADDED" />
       <action android:name="android.intent.action.PACKAGE_REPLACED" />
       <action android:name="android.intent.action.PACKAGE_REMOVED" />

       <!--这里需要scheme为package才可以启动-->
       <data android:scheme="package" />
   </intent-filter>
</receiver>
  • Android 8.0去掉了部分隐式广播,例如网络的变化、app的安装和卸载等,所以不能使用静态注册了,必须使用动态注册
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
appReceiver = AppReceiver()
registerReceiver(appReceiver, intentFilter)
  • 在广播接收者中处理对应的action
class AppReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val packName = intent?.data?.schemeSpecificPart
        when (intent?.action) {
            Intent.ACTION_PACKAGE_ADDED -> {
                Toast.makeText(context, "${packName}应用已安装,哈哈", Toast.LENGTH_SHORT).show()
            }
            Intent.ACTION_PACKAGE_REPLACED -> {
                Toast.makeText(context, "${packName}应用已更新,哈哈", Toast.LENGTH_SHORT).show()
            }
            Intent.ACTION_PACKAGE_REMOVED -> {
                Toast.makeText(context, "${packName}应用已卸载,哈哈", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

发送自定义广播

概念

发送广播是为了通知系统中的应用发生了什么事,一般自己写的软件很少使用自定义广播的,一般都是系统通知其他应用的

  • 自定义广播
private fun sendBroadcastReceiver() {
    val intent = Intent()
    intent.action = "a.b.c"
    sendBroadcast(intent)
}
  • 在清单文件中,注册广播接收者

广播接收者的注册

清单文件注册

广播接收者永远生效,除非卸载应用,或者手动停止进程

代码注册

需要广播接收者时,注册,不需要时反注册,广播就失效了
特殊广播接收者,必须使用代码注册:屏幕点亮和关闭,电量的改变

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值