• Android视图手册之Broadcast广播


    第四篇 Broadcast广播

    Broadcast广播是Android四大组件之一,是一种信息传递机制,通过广播我们能够监听到自身,Android系统,或三方应用之间传递的信息。就像对讲机的频段一样,在同一个频道下,能够利用广播机制进行互相通信。巧妙的利用广播机制,能够让我们的应用实现意想不到的功能。

    概述

    作为Android的四大组件之一的广播,是我们和系统交流的重要桥段,比如系统电量的变化,锁屏,日期变化,插上外部电源等等,都会让系统用广播的形式发出,当我们的应用需求需要去接收这些信息的时候就可以用广播来实现。除了和系统交流,我们也可以利用广播和其他应用,或自身进行交流,其本质是基于AMS(Activity Manager Service)来集中管理的消息传递方式。

    广播的注册

    在这里插入图片描述

    广播接收者的创建

    想要使用广播,我们首先需要先创建一个广播接收者,来处理接收到的广播信息和内容,如下只要继承android.content.BroadcastReceiver,并实现其onReceive方法就可以做到。

    import android.content.BroadcastReceiver;  
    import android.content.Context;  
    import android.content.Intent;  
    import android.util.Log;  
      
    public class MyReceiver extends BroadcastReceiver {  
          
        private static final String TAG = "MyReceiver";  
          
        @Override  
        public void onReceive(Context context, Intent intent) {  
        	String message = intent.getStringExtra("message");
            Log.i(TAG, "onReceive"+message);  
        }  
        
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在onReceive方法内,我们就可以拿到广播而来的Intent中的数据,当然创建了类后我们还需要将广播进行相应的注册,就像加入群组一样,只有加了这个群,我们才能收到这个群的消息

    静态注册

    通过在AndroidManifest.xml文件中配置,我们可以实现静态注册,如下这里的action(android.intent.action.BOOT_COMPLETED)是开机广播的地址,相当于是群id,只有有这个action上我们才能收到对应的广播消息,当然这里的action可以不只一个,我们也可以传入自定义类型的action。其中,静态注册是常驻型的,也就是说当应用关闭后,如果有广播信息传来,我们注册的广播也会被系统调用而自动运行

    <receiver android:name=".MyReceiver">  
       <intent-filter>  
           <action android:name="android.intent.action.BOOT_COMPLETED"/>  
           <action android:name="android.intent.action.MY_BROADCAST"/>  
           <category android:name="android.intent.category.DEFAULT" />  
       </intent-filter>  
    </receiver>  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    不过系统目前很多广播并不支持静态注册,所以在使用静态注册的时候一定要进行了解,如下的一些常用的广播action就不支持。

    广播action描述
    android.intent.action.ACTION_TIME_TICK系统每分钟会发出该广播
    android.intent.action.SCREEN_ON屏幕被点亮之后的广播
    android.intent.action.SCREEN_OFF屏幕被关闭之后的广播
    android.intent.action.BATTERY_CHANGED充电状态,或者电池的电量发生变化//电池的充电状态
    android.intent.action.CONFIGURATION_CHANGED设备当前设置被改变时发出的广播 包括的界面语言,设备方向等
    动态注册

    除了配置的方式,我们也可以在代码中动态注册广播,和静态注册类似,我们也需要指定器广播地址并注册,如下代码所示:

    MyReceiver receiver = new MyReceiver();  
    IntentFilter filter = new IntentFilter();  
    filter.addAction("android.intent.action.MY_BROADCAST"); 
    filter.addAction("android.intent.action.SCREEN_ON"); 
    registerReceiver(receiver, filter);  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中,registerReceiver是android.content.ContextWrapper类中的方法,当我们不是在Activity和Service(这俩都继承了ContextWrapper)中使用的时候,需要传一个继承了ContextWrapper的对象来执行。和静态注册不同的是,我们需要在对调用该注册方法的类进行监听,在其要被销毁的时候进行解除注册,否则系统会抛出一个异常,告诉我们是否未解除该广播,Activity中的解除操作如下:

    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        unregisterReceiver(receiver);  
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    动态注册并不是常驻的,因为解除了该广播,后续我们也接收不到信息了。

    广播发送

    sendBroadcast也是android.content.ContextWrapper类中的方法,我们将所需要传递的信息塞入intent中,并指定其action (这里在实例Intent类方法中传入),这样我们就可以把一个广播发送出去了。

    Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
    intent.putExtra("message", "it's a Broadcast!");  
    sendBroadcast(intent);  
    
    • 1
    • 2
    • 3

    广播类型

    广播传递的形式目前主要分为有序广播(Ordered Broadcast),普通广播(Normal Broadcast),除了这两种外还有已被弃用的 粘性广播(Sticky Broadcast)和实际是通过Handler传递消息的本地广播(因本质不属于广播,本文就不进行介绍了)。

    普通广播

    普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。系统中的广播很多就是以此方式进行传递。
    在这里插入图片描述
    当我们需要发送普通广播时,按下调用发送方法即可:

    Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
    intent.putExtra("message", "it's a Broadcast!");  
    sendBroadcast(intent);  
    
    • 1
    • 2
    • 3
    有序广播

    有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播。其中接收者是可以修改其中要发送的数据,修改和添加都是可以的,这就意味着优先接收者对数据修改之后,下一个接收者接受的数据是上一个接收者已经修改了的。系统广播中的短信就是有序,我们可以通过拦截的方式进行短信拦截,当然,这需要获取到相应的权限。
    在这里插入图片描述
    当我们需要发送有序广播时,按下调用发送方法即可:

    Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
    intent.putExtra("message", "it's a Broadcast!");  
    sendOrderedBroadcast(intent,null);
    
    • 1
    • 2
    • 3

    其中有序广播的优先级,数越大优先级越高(取值范围:-1000~10000),可如下进行配置:

    • 动态注册设置优先级
    MyReceiver receiver = new MyReceiver();  
    IntentFilter filter = new IntentFilter();  
    filter.setPriority(99);//设置优先级
    filter.addAction("android.intent.action.MY_BROADCAST"); 
    registerReceiver(receiver, filter);  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 静态注册设置优先级
    <receiver
                android:name=".MyReceiver"
                android:priority="59" 
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="not_order_broadcast" />
                </intent-filter>
            </receiver>
            <!--
            priority:这个属性设置优先级
            exported:这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。
             如果设置为true,则能够被调用或交互,否则不能。设置为false时,
            只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务
            -->
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 将广播接收的数据可以修改再传递,或直接拦截不再传递
    public class MyReceiver extends BroadcastReceiver {
        public MyReceiver() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if ("MY_BROADCAST".equals(intent.getAction())){
            	String message = intent.getStringExtra("message");
            	if(message.equal("Next")){
            		//修改内容
    	            Bundle bundle = new Bundle();
    	            bundle.putString("message", "内容有变动");
    	          	setResultExtras(bundle);
                }else{
                	//阻断广播的发送到下一个
    				abortBroadcast();
    			}
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    粘性广播

    粘性广播的消息在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。需注意的是,粘性广播只要不移除,就一直在内存当中,可能是这个原因,粘性广播目前已弃用,存在安全隐患。在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完Receiver后就可以继续使用刚才的广播。如果在接收者注册完成前发送了多条相同Action的粘性广播,注册完成后只会收到一条该Action的广播,并且消息内容是最后一次广播内容。系统网络状态的改变发送的广播就是粘性广播。

    在这里插入图片描述

    发送粘性广播需要如下权限

    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
    
    • 1

    当我们需要发送有序广播时,按下调用发送方法即可:

    Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
    intent.putExtra("message", "it's a Broadcast!");  
    sendStickyBroadcast(intent,null);
    
    • 1
    • 2
    • 3

    小知识

    1、部分广播不能进行静态注册的原因

    • 提高系统效率:有些广播的行为是Android系统中经常发生的,比如屏幕开关,每分钟发生广播,如果大多数程序监听,会大大的拖慢整个系统,所以android不鼓励我们在后台监听这些广播。
    • 因为有序广播的优先级问题。静态注册时,系统的优先级大于应用,并且系统阻止了广播的向下传播。又因在Android 的广播机制中,动态注册的优先级是要高于静态注册优先级的。故用动态注册代替静态注册。
    • 因为常驻后台,可能会影响系统安全,像之前的一些不断启动,然后提供支付界面来勒索的流氓软件,很多就是借用广播的机制来实现的。

    2、广播里建议不做耗时操作
    程序的响应(Responsive)被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视,当BroadcastReceiver在10秒内没有执行完毕,Android会认为该程序无响应,所以在 BroadcastReceiver里不能做一些比较耗时的操作,否则会弹出ANR(Application No Response)的对话框。其中,粘性广播不受10秒约束

    可能遇到的相关问题

    1、广播的生命周期状态是怎么样的?
    广播的生命周期其实很短,只有当它接收广播的时候才会被实例化出来,一旦执行完 onReceive() 方法后就会被销毁。
    2、广播能被多次注册吗?
    可以的,同一个广播其实是可以被多次注册,所以在使用的过程中有些开发者就会遇到同时接收多个重复的消息,而实际上只发送了一个广播,这就是因为广播被重复注册了。

    面试技巧之start法则

    当我们进行项目经历表达时,往往可能容易抓不到重点,我们可以通过start法则来进行有条不紊的回答。
    start法则:
    ”S“——即是situation。就是你参加某些工作所处于的背景和环境等是如何的?比如做校招,是基于公司战略发展和各个部门的人员需求下进行的;

    ”T“——即是Task。就是你在这个工作中所承担的任务或者职责。比如在校招中,你主要负责整个校招活动的策划和宣传。

    ”A“——即是Action。就是你在这个工作中基于任务而采取什么样的措施或应对方法。比如在校招中,你先通过电话联系各个学校的就业办进行确认,随后上门拜访,确认好校招的地址,最后把需要宣传的工作做好,比如宣传的方式?所需的材料等等。

    ”R“——即是Result。就是你在整个工作中得到什么样的结果。比如那次校招你招到多少人,此外在这个校招中,你收获到什么,总结了哪些。

  • 相关阅读:
    月薪5万以上的项目经理都达到了哪些标准?
    负载均衡算法实现
    Java学习笔记44——Stream流
    力扣:15-三数之和
    lighttpd cgi不能重启
    渗透测试流程是什么?7个步骤给你讲清楚!
    C之(10)CMocka-单元测试框架使用
    使用antv/X6实现流程图(包括线条动画流动,路径标签,悬浮窗口等)快速搭建流程图,DAG图等初始实践记录
    【Web开发 | Django】数据库分流之道:探索Django多数据库路由最佳实践
    数据结构与算法之美学习笔记:29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?
  • 原文地址:https://blog.csdn.net/number_cmd9/article/details/125417486