ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

《Android》Chap.6 广播机制BroadcastReceiver

2022-01-18 19:00:34  阅读:207  来源: 互联网

标签:override 广播 intent context fun Chap.6 Android BroadcastReceiver


广播机制简介

Android中的每个应用程序都可以对自己感兴趣的⼴播进行注册,这样该程序就只会收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其他应用程序的。
Android提供了⼀套完整的API,允许应用程序自由地发送和接收广播。
接收广播的方法:BroadcastReceiver

分类

  • 标准广播:是⼀种完全异步执行的广播,在广播发出之后,所有的BroadcastReceiver几乎会在同⼀时刻收到这条广播消息,因此它们之间没有任何先后顺序可⾔。这种⼴播的效率会比较⾼,但同时也意味着它是无法被截断的。
  • 有序广播:则是⼀种同步执行的广播,在广播发出之后,同⼀时刻只会有⼀个BroadcastReceiver能够收到这条广播消息,当这个BroadcastReceiver中的逻辑执行完毕后,广播才会继续传递。所以此时的BroadcastReceiver是有先后顺序的,优先级高的BroadcastReceiver就可以先收到广播消息,并且前面的BroadcastReceiver还可以截断正在传递的广播,这样后面的BroadcastReceiver就无法收到广播消息了。

接收系统广播

注册BroadcastReceiver的方法:

  • 动态注册:代码中注册,可以自由的控制注册与注销,但只能在程序启动之后才能接受广播。
  • 静态注册:在AndroidManifest.xml中注册,不允许接收隐式广播

动态注册监听时间变化

新建⼀个继承于BroadcastReceiver的类,并重写父类的onReceive()方法。
当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。

class MainActivity : AppCompatActivity() {

    private lateinit var mainbinding: ActivityMainBinding

    lateinit var timeChangeReceiver: TimeChangeReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //使用ViewBinding的标准写法
        mainbinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mainbinding.root)

        //在action中设定想要接受的广播类型
        val intentFilter = IntentFilter()
        intentFilter.addAction("android.intent.action.TIME_TICK") //时间变化时广播的值为“”
        timeChangeReceiver = TimeChangeReceiver() //创建实例
        registerReceiver(timeChangeReceiver,intentFilter) //进行注册

    }

    override fun onDestroy() {
        super.onDestroy()
        //动态注册的BroadcastReceiver最后一定要取消注册
        unregisterReceiver(timeChangeReceiver)
    }

    inner class TimeChangeReceiver : BroadcastReceiver(){ //内部类
        override fun onReceive(context: Context?, intent: Intent?) { //接收到广播后执行
            Toast.makeText(context,"Time has changed",Toast.LENGTH_SHORT).show()
        }

    }
}

在这里插入图片描述

快捷方式创建BroadcasrReceiver

快速创建BroadcasrReceiver
在这里插入图片描述
弹出窗口:
在这里插入图片描述

静态注册实现开机启动

用上述方式新建BootCompleteReceiver

class BootCompleteReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show()
    }
}

AndroidManifest.xml中注册

<receiver
    android:name=".BootCompleteReceiver"
    android:enabled="true"
    android:exported="true">
</receiver>

上述的快捷方式会帮助你自动注册
在这里插入图片描述
紫色箭头指向的<uses-permission>标签用于声明权限
橙色箭头指向的<intent-filter>标签用于声明相应的action

注意

不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作
BroadcastReceiver中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序会出现错误。

发送自定义广播

标准广播

首先需要一个BroadcastReceiver来接受这个广播

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"receiver in MyBroadCastReceiver", Toast.LENGTH_SHORT).show()
    }
}

AndroidManifest.xml中注册,并声明接受广播的值
在这里插入图片描述
activity_main.xml中添加一个button用于作为发送代码的触发点

<Button
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="send BroadCast"
    />

MainActivity中设置点击事件

mainbinding.button.setOnClickListener {
    val intent = Intent("com.example.broadcasttestkt.MY_BROADCAST")
    intent.setPackage(packageName) //传入当前应用程序的包名
    sendBroadcast(intent) //发送标准广播
}

在这里插入图片描述

注意

在Android 8.0系统之后,静态注册的BroadcastReceiver是无法接收隐式广播的,而默认情况下我们发出的自定义广播恰恰都是隐式广播。因此这里⼀定要调用setPackage()方法,指定这条广播是发送给哪个应用程序的,从而让它变成⼀条显式广播,否则静态注册的BroadcastReceiver将无法接收到这条广播。

标准广播的工作流程实践

首先需要再新建一个BroadcastReceiver来接受这个广播

class AnotherBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"receiver in MyBroadCastReceiver", Toast.LENGTH_SHORT).show()
    }
}

AndroidManifest.xml中注册,并声明接受广播的值
在这里插入图片描述
再点击button时,会分别弹出两次Toast
在这里插入图片描述在这里插入图片描述
可以说明在标准广播发出后,所有的BroadcastReceiver几乎会在同时接受到这个广播消息

有序广播

将标准广播改为有序广播只需要改动一行代码

mainbinding.button.setOnClickListener {
    val intent = Intent("com.example.broadcasttestkt.MY_BROADCAST")
    intent.setPackage(packageName)
    //sendBroadcast(intent)  //发送标准广播
    sendOrderedBroadcast(intent,null) //发送有序广播 第二个参数是权限相关字符串
        }

AndroidManifest.xml中设置接受广播的优先级,优先级高的先接收
在这里插入图片描述
在优先级高的MyBroadcastReceiver中设置广播截断

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"receiver in MyBroadCastReceiver", Toast.LENGTH_SHORT).show()
        abortBroadcast() //广播截断
    }
}

再次点击button时就只有只弹出一次消息提示了
在这里插入图片描述

最佳实践:强制下线功能

首先创建ActivityCollector类,用于管理所有的Activity

object ActivityCollector {

    private val activities = ArrayList<Activity>()

    fun addActivity(activity: Activity){
        activities.add(activity)
    }

    fun removeActivity(activity: Activity){
        activities.remove(activity)
    }

    fun finishAll(){
        for (activity in activities){
            if (!activity.isFinishing){
                activity.finish()
            }
        }
        activities.clear()
    }

}

创建BaseActivtiy类作为所有Activity的父类

open class BaseActivity : AppCompatActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityCollector.addActivity(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }

}

编写activity_login.xml作为登录界面

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".LoginActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Account:" />

        <EditText
            android:id="@+id/accountEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical" />

    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Password:" />

        <EditText
            android:id="@+id/passwordEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical" />

    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_gravity="center_horizontal"
        android:text="login" />

</LinearLayout>

在这里插入图片描述

LoginActivity中编写登录逻辑

class LoginActivity : BaseActivity() {

    private lateinit var loginBinding: ActivityLoginBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        loginBinding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(loginBinding.root)

        loginBinding.login.setOnClickListener {
            val account = loginBinding.accountEdit.text.toString() //获取用户输入用户名
            val password = loginBinding.passwordEdit.text.toString() //获取用户输入密码
            if (account == "admin" && password == "123456"){
            	//如果用户名密码正确,使用intent跳转到MainAvtivity
                val intent = Intent(this,MainActivity::class.java)
                startActivity(intent)
                finish()
            }else{
            	//用户名密码错误提示
                Toast.makeText(this,"account or password is invalid",Toast.LENGTH_SHORT).show()
            }
        }
    }
}

activity_main.xml中添加一个button,并在MainActivity中编写点击事件

class MainActivity : BaseActivity() {

    private lateinit var mainBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mainBinding.root)

        mainBinding.forceOffline.setOnClickListener {
            //点击按钮,发送广播
            val intent = Intent("com.example.broadcastbestpractices.FORCE_OFFLINE")
            sendBroadcast(intent)
        }
    }
}

BroadcastReceiver中需要弹出⼀个对话框来阻塞用户的正常操作,但如果创建的是⼀个静态注册的BroadcastReceiver,是没有办法在onReceive()方法里弹出对话框这样的UI控件的,而也不可能在每个Activity中都注册⼀个动态的BroadcastReceiver
所以解决方案是:BaseActivity中动态注册⼀个BroadcastReceiver就可以了,因为所有的Activity都继承自BaseActivity

open class BaseActivity : AppCompatActivity(){

    lateinit var receiver: ForceOfflineReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityCollector.addActivity(this)
    }

    override fun onResume() {
        super.onResume()
        val intentFilter = IntentFilter()
        intentFilter.addAction("com.example.broadcastbestpractices.FORCE_OFFLINE")
        receiver = ForceOfflineReceiver()
        registerReceiver(receiver,intentFilter)
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(receiver)
    }

    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }

    inner class ForceOfflineReceiver : BroadcastReceiver(){
        override fun onReceive(context: Context?, intent: Intent?) {
            AlertDialog.Builder(context!!).apply { //构建对话框
                setTitle("Warning")
                setMessage("You are forced to be offline.Please try to login again.")
                setCancelable(false) //对话框设置为不可取消
                setPositiveButton("OK"){ _, _ ->
                    //注册确定按钮 销毁所有Activity并重新启动LoginActivity
                    ActivityCollector.finishAll()
                    val restartIntent = Intent(context,LoginActivity::class.java)
                    context.startActivity(restartIntent)
                }
                show()
            }
        }

    }
}

在这里插入图片描述

标签:override,广播,intent,context,fun,Chap.6,Android,BroadcastReceiver
来源: https://blog.csdn.net/m0_50754064/article/details/122554986

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有