Android入门(11)| 全局广播与本地广播_android本地广播和全局广播-程序员宅基地

技术标签: java  apache  android  Android  


广播概念

Android 中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自系统的,也可能是来自于其他应用程序的。

广播有两种类型——有序广播和标准广播:

  • 标准广播: 一种完全异步执行的广播,在广播发出去之后,所有的广播接收器几乎都会同一时刻接收到这条广播消息,没有任何的先后顺序可言,这种广播的效率比较高,但是无法被截断

在这里插入图片描述

  • 有序广播: 是一种同步执行的广播,在广播发出去之后,同一时刻只会有一个广播接收器能够收到这条消息,当这个广播接收器中的逻辑执行完毕之后,广播才会继续传递,所以这时候的广播接收器是有优先级顺序的,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播就无法收到广播消息。

在这里插入图片描述

接收广播

动态注册

Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。比如手机开机、电池电量发生变化、时间或者时区发生改变等等。如果想要接收到这些广播就需要使用广播接收器。

注册广播的方式一般也有两种,在 代码中注册(动态注册) 或者 AndroidManifest.xml 中注册(静态注册)

实例

我们实现一个能准确地告诉用户当前有没有网络的功能,在实现代码前,由于 Android 为了保护用户设备的隐私和安全,规定了程序需要进行一些对用户来说比较敏感的操作,必须在配置文件中声明权限才可以,该功能监听了网络的变化,所以必须在 AndroidManifest.xml 配置权限才能访问系统网络状态:
在这里插入图片描述

public class BroadcastActivity extends AppCompatActivity {
    
    private IntentFilter intentFilter; // 意图过滤器,旨在匹配可以响应对应操作的组件
    private NetworkChangeReceiver networkChangeReceiver; // 自定义的广播接收器类

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        intentFilter = new IntentFilter();
        // android.net.conn.CONNECTIVITY_CHANGE是网络状态变化时系统的广播
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

        networkChangeReceiver = new NetworkChangeReceiver();
        // 注册,将接收器与IntentFilter相匹配
        registerReceiver(networkChangeReceiver, intentFilter);
    }

    // 动态注册一定要在结束时取消注册
    protected void onDestroy(){
    
        super.onDestroy();
        // 实现取消注册
        unregisterReceiver(networkChangeReceiver);
    }

    // 自定义的广播接收器类
    class NetworkChangeReceiver extends BroadcastReceiver{
    

        @Override
        public void onReceive(Context context, Intent intent) {
    
            // 通过getSystemService得到管理网络连接的connectivityManager实例
            ConnectivityManager connectivityManager = (ConnectivityManager)
                    getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            // 通过isAvailable方法判断是否有网络
            if(networkInfo != null && networkInfo.isAvailable()){
    
                Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
            } else{
    
                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

静态注册

动态注册的广播可以自由控制注册、注销,很灵活,缺点是必须在程序启动之后才能接受到广播,因为注册的逻辑是写在 onCreate() 里面的,使用静态注册可以让程序在未启动的情况下接受到广播

实例

静态注册需要在 AndroidManifest.xml 进行注册,使用 receiver 标签,并告诉这个 receiver 注册哪一个 action,下面是一个开机启动接受广播的案例:

在这里插入图片描述

public class BootCompleteReceiver extends BroadcastReceiver {
    

    @Override
    public void onReceive(Context context, Intent intent) {
    
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
    }
}

PS:不要在 onReceive() 方法中添加过多逻辑或耗时操作,因为广播接收器中不允许开启线程,因此当 onReceive() 运行较长时间却未结束时,程序就会报错。


发送广播

发送标准广播

发送广播使用 Intent 进行发送,首先需要准备一个接收器用于接受发送的广播:

public class MyBroadcastReceiver extends BroadcastReceiver {
    

    @Override
    public void onReceive(Context context, Intent intent) {
    
        // 收到自定义广播时会弹出提示
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_LONG).show();
    }
}

AndroidManifest.xml 中注册广播的值:

实现点击 BroadcastActivity 活动中的 send broadcast 按钮来发送广播:

布局文件 broad_layout.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button_broadcast1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="send broadcast"/>
</LinearLayout>

活动代码 BroadcastActivity.java

public class BroadcastActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.broad_layout);

        Button button = findViewById(R.id.button_broadcast1);
        button.setOnClickListener((View v)->{
    
            // 将要发送的广播植入Intent
            Intent intent = new Intent("com.example.activitytest.Activity.MY_BROADCAST");
            // 参数1:包名;参数2:接收器的路径
            ComponentName componentName = new ComponentName("com.example.activitytest",
                    "com.example.activitytest.Activity.MyBroadcastReceiver");
            // 通过调用Intent中的setComponent方法,我们可以打开另外一个应用中的Activity或者服务。
            intent.setComponent(componentName);
            // 调用Context的sendBroadcast方法发送广播
            sendBroadcast(intent);
        });
    }
}

PS: ComponentName 构造函数的第一个参数指的包名是 AndroidManifest.xml 文件下 package 属性对应的包名:
在这里插入图片描述
而非 BroadcastActivity.java 文件所在的包名:
在这里插入图片描述
详见该博客

运行结果:
在这里插入图片描述


广播的跨进程特性

新建一个项目,创建广播接收器 MyReceiver.java

public class MyReceiver extends BroadcastReceiver {
    
    private static final String TAG = "MyReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
    
        Log.e(TAG, "onReceive: gone");
        Toast.makeText(context, "received gone", Toast.LENGTH_LONG).show();
    }
}

AndroidManifest.xml
在这里插入图片描述
在原来项目的 BroadcastActivity.java 文件中发送第二条广播:
在这里插入图片描述
运行结果:
在这里插入图片描述
在这里插入图片描述
更多关于广播的问题详见本文——解决 Android 8.0 以上静态广播无法注册


发送有序广播

很多人对之前的代码可能会有疑问,我指定广播发送给哪个包的哪个接收器,这还算“广播”吗?因此,对于安卓高版本而言,还有另一种发送广播的方法:

修改 BroadcastActivity.java 中的代码:
在这里插入图片描述

即可实现真正意义上的广播。在此基础上,我们发送有序广播,定义接收器的优先级
在这里插入图片描述
并在接收器 MyBroadcastReceiver.java 中截断广播,不允许广播继续传递:

public class MyBroadcastReceiver extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
    
        // 收到自定义广播时会弹出提示
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
        // 截断
        abortBroadcast();
    }
}

不必对另一个接收器 MyReceiver 进行更改,此时就已达到了只有 MyBroadcastReceiver 能收到广播,而 MyReceiver 不能收到广播的目的。


本地广播

前面我们发送和接受的广播都是系统的全局广播,发出的广播可以被其他任何应用程序接收到。这样容易引起安全问题,为了解决安全性问题,Android 支持发送本地广播,其有以下特点:

  • 广播不会离开我们的程序,无需担心机密数据泄露;
  • 其他程序的广播无法发送到我们程序内部,无需担心有安全漏洞的隐患;
  • 比发送全局广播更高效。
  • 本地广播的接收只能使用动态注册,因为静态注册就是为了让程序在未启动的时候也能接收到广播,而发送本地广播的时候应用程序肯定启动了

本地广播并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并且提供了发送广播和注册广播接收器的方法:

public class BroadcastActivity extends AppCompatActivity {
    
    private static final String TAG = "BroadcastActivity";
    private IntentFilter intentFilter; // 意图过滤器
    private LocalReceiver localReceiver; // 自定义接收器类
    private LocalBroadcastManager localBroadcastManager; // support包提供的本地广播工具

    @SuppressLint("WrongConstant")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.broad_layout);

        localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例

        Button button = findViewById(R.id.button_broadcast1);
        button.setOnClickListener((View v)->{
    
            Log.e(TAG, "onCreate: start");
            // 将要发送的广播植入Intent
            Intent intent = new Intent("com.example.activitytest.Activity.LOCAL_BROADCAST");
            if(Build.VERSION.SDK_INT >= 28){
    
                // 突破隐式广播限制
                intent.addFlags(0x01000000);
            }
            localBroadcastManager.sendBroadcast(intent); // 发送本地广播
        });

        // 动态注册的步骤
        intentFilter = new IntentFilter();
        // 添加自定义广播
        intentFilter.addAction("com.example.activitytest.Activity.LOCAL_BROADCAST");
        // 实例化接收器
        localReceiver = new LocalReceiver();
        // 注册接收器
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }

    // 动态注册一定要在结束时取消注册
    @Override
    protected void onDestroy() {
    
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }

    class  LocalReceiver extends BroadcastReceiver {
    

        @Override
        public void onReceive(Context context, Intent intent) {
    
            Toast.makeText(context, "本地广播", Toast.LENGTH_LONG).show();
        }
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Jormungand_V/article/details/123069054

智能推荐

githup的使用方法-程序员宅基地

Mac 版本下ssh-keygen -t rsa -C"[email protected]”生成.ssh文件,文件夹下有id_rsa,id_rsa.pub两个文件,所要用的key 值就在id_rsa.pub文件中,复制粘贴到githup即可,如果找不到ssh文件,命令行open~/.ssh cd ~/.ssh 测试是否与远程库建立了连接 ssh -T [email protected]_githup

使用Zip Utils 解压文件的操作示例_openzip-程序员宅基地

采用Zip Utils解压文件的基本操作示例及函数详解。1.HZIP OpenZip(const TCHAR *fn, const char *password); 参数fn文件路径,相对和绝对路径应该都可以,支持unicode格式 password是解压的密码,可以为空,只需传入参数0即可。但是遗憾的是,密码只支持ascii格式,没办法,只好自己封装了两个函数用于单字节和宽字节之间互相转换_openzip

如何将JavaScript数组转换为JSON-程序员宅基地

在JavaScript中,您可以使用JSON.stringify将数组或值转换为JSON格式的字符串。 var output = {}output[0] = "a";output[1] = "b";output[2] = "c";console.log( JSON.stringify(output) ); 输出量 { "0":"a", "1":"b", "2":"c"..._js将数组转为json字符串

android服务器apk,android – 从服务器下载APK并将其安装到设备-程序员宅基地

01-13 12:06:51.562: W/PackageParser(4364): Unable to read AndroidManifest.xml of /mnt/sdcard/example.apk01-13 12:06:51.562: W/PackageParser(4364): java.io.FileNotFoundException: AndroidManifest.xml01-..._android服务下载安装

HDU1052 Tian Ji -- The Horse Racing-程序员宅基地

题目描述田忌赛马问题,田忌和王都有n匹马。n场比赛(每匹马都只能上场一次)。每场比赛输的要给赢的人200块钱,问最后田忌最多能获得多少钱。输入n,接着是n匹田忌马速度,接着是n匹王的马速度,遇0结束。样例输入392 83 7195 87 74220 2020 20220 1922 180输出20000思路:

php通过浏览器下载json文件遇到的问题_通常不会下载json-程序员宅基地

一、前言 最近要做一下json文件下载的功能,就是点击一个按钮,然后执行下载操作。本来这种下载应该是很简单的,不过博主在实际操作的时候,还是遇到了不少问题,记录一下。二、ajax不能下载文件1、为什么不能下载 如标题所示,本来是通过ajax请求链接下载..._通常不会下载json

随便推点

将d盘文件复制到linux服务器,怎么把d盘的东西移到e盘-程序员宅基地

大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答。把d盘的东西移到e盘直接用鼠标右键剪切复制就可以了,在d盘剪切,到e盘复制即可。如果担心文件丢失,也可以先复制,复制成功以后再删除d盘的文件。盘符是DOS、WINDOWS系统对于磁盘存储设备的标识符。一般使用26个英文字符加上一个冒号:来标识。由于历史的原因,早期的PC机一般装有两个软盘驱动器,所以,“A:”和“B:”这两个盘符就..._怎么把d盘的东西移到e盘

matlab加入混响,基于matlab音乐混响效果实现.pdf-程序员宅基地

基于matlab音乐混响效果实现基于 MATLAB 的语音混响效果的设计与实现班级:10 通信二班姓名:彭海军学号:14102301154摘要 (中文)数字信号处理(Digital Signal Processing)技术,从 20 世纪 60 年代以来,随着计算机科学和信息科学发展,数字处理技术应运而生 得..._卷积混响 matlab

技术至简-6:傅里叶分析中直流与交流、离散与连续、非周期与周期辨证统一-程序员宅基地

在傅里叶分析时域信号,需要区分直流与交流分量,区分时域的离散信号与连续信号,区分时域的周期信号与非周期信号。其实他们是辨证统一的,它们通过时间T的无穷大和无穷小的概念实现了统一。时间时间长度是一种客观的存在,时间的长度也是一种观察者的感受。100年有时候太长,100年有时候也太短。无限大:空间的无穷大,大到宇宙万物。时间的无穷大,大到几百亿年。无穷小空间的无穷小,小到分子、原子、粒子。时间的无穷小,小到一眨眼,以及过去几百万个时间单位,几十亿的时间单..

word如何关闭批注模式【教程】_word如何不显示批注-程序员宅基地

退出word文档的批注编辑的方法如下(以windows10系统的word2018版为例,需要word处于批注编辑模式):1、打开word文档,点击界面上方的“审阅”按钮。2、随后即可看到批注正在处于编辑状态,随后点击“显示批注”按钮。3、在word文档主界面中点击任意一个空白的位置。4、随后即可发现批注编辑模式已经被退出了,可以在word文档上正常打字了。..._word如何不显示批注

Jmeter 跳过用例执行的两种方式_jmeter判断获取接口没有这个值跳过执行-程序员宅基地

1.当一个字段只有一个值的时候使用if判断某字段是否为空格式例如 "${utoken}" != "\${utoken}"图中的意思就是当utoken不为空时,则执行该if条件下的用例2.当一个字段有多个值的时候使用ForEach控制器,应用场景如图:此时,如果返回结果中没有id,则forEach不会运行,达到了跳过的目的。..._jmeter判断获取接口没有这个值跳过执行

php调用酷狗音乐APi-程序员宅基地

直入主题最近在m.kugou.com 抓到了酷狗api功能包括:1.获取歌手头像,2.获取下载链接,3.获取歌词4.。。。(0)回复1楼2015-11-07 20:06举报 |个人企业举报垃圾信息举报热门推荐千锋php培训 课程全新升级 g

推荐文章

热门文章

相关标签