安卓开发_深入理解广播机制

一、Broadcast(广播)

在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,如果某个程序接收了这个广播,就会做相应的处理。这个广播跟我们传统意义中的电台广播有些相似之处。之所以叫做广播,就是因为它只负责“说”而不管你“听不听”,也就是不管你接收方如何处理。另外,广播可以被不只一个应用程序所接收,当然也可能不被任何应用程序所接收。

(百度百科)

二、BroadcastReceiver(广播接收器)

1、自定义BroadcastReceiver

自定义广播接收器继承基类BroadcastReceivre,并实现抽象方法onReceive(context, intent)方法。

自定义广播接收器接收到相应广播后,会自动回调onReceive(context, intent)方法。

onReceive方法中不能执行太耗时的操作。否则将因此ANR。

原因如下:

(1)广播接收器的生命周期是非常短暂的,当广播接收器接收到广播时开始创建,而执行完onReceive(context,intent)方法后就销毁

(2)默认情况下,广播接收器也是运行在UI线程中的,即不建议执行耗时操作

注:在广播接收器中创建子线程执行耗时操作也是不建议使用的,因为一旦广播接收器被销毁(太容易被销毁了,生命周期太短),子线程就成为了空线程,很容易被系统杀死

2、自定义BroadcastReceiver 可以执行的操作

(1)Toast 

 (2) 发布通知栏信息 

 (3) 对话框 (注意上下文 必须是一个Activity,因为对话框必须依赖于Activity存在)

 (4) 发送广播

 (5) 开启服务

 (6) 开启新的Activity

3、自定义BroadcastReceiver 注册

(1)静态注册

 即在清单文件中注册 

android:name="" 为包名+类名(自定义BroadcastReceiver类)
<intent-filter></intent-filer> 过滤器  在其中的action都是可以通过的
action android:name="" 动作.可以是系统动作,也可以是自定义的定动作
1 <receiver android:name="com.xqx.mybroadcast.RootReceiver">
2    <intent-filter>
3           <action android:name="android.intent.action.BOOT_COMPLETED"/>
4       <action android:name="android.Intent.ACTION_SCREEN_OFF"/>
5    </intent-filter>
6 </receiver>

注:

关于静态注册广播 应用程序退出之后,相应的广播接收器是否还能接受广播的情况(比如一个应用程序里有一个广播接收器接收是“收到短信”的广播,当应用程序退出后,还能收到这个广播么?)

答案:

3.1版本之前可以,3.1版本以后不可以

3.1版本之后,系统在广播和Intent相关的FLAG参数做出了两个新参数

FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)

FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

主要原因如下:

自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。

在发送广播时,不管是什么广播类型(系统广播),系统默认增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES(不包含已经停止的包)的FLAG,

导致即使是静态注册的广播接收器,对于其所在进程已经退出的应用程序,同样无法接收到广播。

当然,即使系统给所有的广播一开始都默认了FLAG值为:FLAG_EXCLUDE_STOPPED_PACKAGES (不发送给已经停止的应用程序的广播接收器)

导致已经退出的应用程序无法接收到系统广播          (由于是系统内部直接发出,无法更改此intent flag值)

但是我们自定义的广播却可以设置这个FLAG值,使应用程序结束后,也可以接收到广播

1                     Intent intent=new Intent("com.xqx.broadcast.xxx");
2                     intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
3                     
4                     intent.putExtra(Config.EXTRA_TIME, time++);
5                     
6                     sendBroadcast(intent); //发送广播

对于动态注册类型的BroadcastReceiver,由于此注册和取消注册实在其他组件(如Activity)中进行,因此,不熟FLAG参数变化的影响。

(2)动态注册

  即在代码中注册 (注意Intent.ACTION_BATTERY_CHANGED 电量发生变化广播   必须动态注册)

  (2.1)、首先要有一个自定义的 BroadcastReceiver 类

1 public class RootReceiver extends BroadcastReceiver {
2 
3     @Override
4     public void onReceive(Context context, Intent intent) {
5                  //这里写接收到广播后执行的操作  
6     }
7 }

 (2.2)、实例化这个对象

RootReciver receiver = new RootReciver();

  (2.3)、动态动态注册广播

registerReceiver(receiver, new IntentFilter("com.xqx.broadcast.xxx"));

注:

Android中所有与观察者模式有关的设计中,一旦涉及到register,必定需要unregister。

因此,上例在onDestroy()回调中需要unregisterReceiver(receiver)。

当此Activity实例化时,会动态将RootReciver注册到系统中。当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。

4、发送广播

广播 其实是以 意图(Intent)的形式表示的

发送广播 就是 通过广播发送者将”意图“(意图包含action 附带数据)发送出去。被相应的BroadcastReceiver接收后将会回调onReceive()函数。

1 Intent intent=new Intent("com.xqx.broadcast.xxx"); 
2 intent.putExtra(Config.EXTRA_TIME, time++); 
3 sendBroadcast(intent);

看一个广播的Demo  应用程序发送广播 ,应用程序内的广播接收器接收广播并获得广播的数据

 1 package com.example.demo01;
 2 
 3 import android.os.Bundle;
 4 import android.app.Activity;
 5 import android.content.BroadcastReceiver;
 6 import android.content.Context;
 7 import android.content.Intent;
 8 import android.content.IntentFilter;
 9 import android.view.Menu;
10 import android.widget.Toast;
11 
12 public class MainActivity extends Activity {
13 
14     private MyReceiver receiver;
15     
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20         
21         //实例化自定义广播接收器对象
22         receiver = new MyReceiver();
23         //动态注册广播接收器
24         registerReceiver(receiver, new IntentFilter("com.xqx.mybrodcast"));
25         
26         //发送广播
27         Intent intent = new Intent("com.xqx.mybrodcast");
28         intent.putExtra("key", "hello world!");
29         sendBroadcast(intent);
30         
31     }
32 
33     
34     
35     
36     //自定义广播接收器
37     class MyReceiver extends BroadcastReceiver{
38 
39         @Override
40         public void onReceive(Context context, Intent intent) {
41             //自定义广播接收器接收到广播后执行的操作
42             String key = intent.getStringExtra("key");
43             Toast.makeText(getApplicationContext(), "接收到广播,广播附带信息为:"+key, 1).show();
44         }
45     }
46 }

三、广播分类

广播分为:

系统广播、自定义

普通、有序广播、粘性广播

应用程序内部广播

1、系统广播,即系统定义的广播,注意3.1版本之后应用程序退出后无法接收到系统广播了

 1 String ADD_SHORTCUT_ACTION 动作:在系统中添加一个快捷方式。  
 2 String ALL_APPS_ACTION 动作:列举所有可用的应用。输入:无。  
 3 String ALTERNATIVE_CATEGORY 类别:说明 activity 是用户正在浏览的数据的一个可选操作。  
 4 String ANSWER_ACTION 动作:处理拨入的电话。  
 5 String BATTERY_CHANGED_ACTION 广播:充电状态,或者电池的电量发生变化。  
 6 String BOOT_COMPLETED_ACTION 广播:在系统启动后,这个动作被广播一次(只有一次)。  
 7 String BROWSABLE_CATEGORY 类别:能够被浏览器安全使用的 activities 必须支持这个类别。  
 8 String BUG_REPORT_ACTION 动作:显示 activity 报告错误。  
 9 String CALL_ACTION 动作:拨打电话,被呼叫的联系人在数据中指定。  
10 String CALL_FORWARDING_STATE_CHANGED_ACTION 广播:语音电话的呼叫转移状态已经改变。  
11 String CLEAR_CREDENTIALS_ACTION 动作:清除登陆凭证 (credential)。  
12 String CONFIGURATION_CHANGED_ACTION 广播:设备的配置信息已经改变,参见 Resources.Configuration.  
13 Creator CREATOR 无 无  
14 String DATA_ACTIVITY_STATE_CHANGED_ACTION 广播:电话的数据活动(data activity)状态(即收发数据的状态)已经改变。  
15 String DATA_CONNECTION_STATE_CHANGED_ACTION 广播:电话的数据连接状态已经改变。  
16 String DATE_CHANGED_ACTION 广播:日期被改变。  
17 String DEFAULT_ACTION 动作:和 VIEW_ACTION 相同,是在数据上执行的标准动作。  
18 String DEFAULT_CATEGORY 类别:如果 activity 是对数据执行确省动作(点击, center press)的一个选项,需要设置这个类别。  
19 String DELETE_ACTION 动作:从容器中删除给定的数据。  
20 String DEVELOPMENT_PREFERENCE_CATEGORY 类别:说明 activity 是一个设置面板 (development preference panel).  
21 String DIAL_ACTION 动作:拨打数据中指定的电话号码。  
22 String EDIT_ACTION 动作:为制定的数据显示可编辑界面。  
23 String EMBED_CATEGORY 类别:能够在上级(父)activity 中运行。  
24 String EMERGENCY_DIAL_ACTION 动作:拨打紧急电话号码。  
25 int FORWARD_RESULT_LAUNCH 启动标记:如果这个标记被设置,而且被一个已经存在的 activity 用来启动新的 activity,已有 activity 的回复目标 (reply target) 会被转移给新的 activity。  
26 String FOTA_CANCEL_ACTION 广播:取消所有被挂起的 (pending) 更新下载。  
27 String FOTA_INSTALL_ACTION 广播:更新已经被确认,马上就要开始安装。  
28 String FOTA_READY_ACTION 广播:更新已经被下载,可以开始安装。  
29 String FOTA_RESTART_ACTION 广播:恢复已经停止的更新下载。  
30 String FOTA_UPDATE_ACTION 广播:通过 OTA 下载并安装操作系统更新。  
31 String FRAMEWORK_INSTRUMENTATION_TEST_CATEGORY 类别:To be used as code under test for framework instrumentation tests.  
32 String GADGET_CATEGORY 类别:这个 activity 可以被嵌入宿主 activity (activity that is hosting gadgets)。  
33 String GET_CONTENT_ACTION 动作:让用户选择数据并返回。  
34 String HOME_CATEGORY 类别:主屏幕 (activity),设备启动后显示的第一个 activity。  
35 String INSERT_ACTION 动作:在容器中插入一个空项 (item)。  
36 String INTENT_EXTRA 附加数据:和 PICK_ACTIVITY_ACTION 一起使用时,说明用户选择的用来显示的 activity;和 ADD_SHORTCUT_ACTION 一起使用的时候,描述要添加的快捷方式。  
37 String LABEL_EXTRA 附加数据:大写字母开头的字符标签,和 ADD_SHORTCUT_ACTION 一起使用。  
38 String LAUNCHER_CATEGORY 类别:Activity 应该被显示在顶级的 launcher 中。  
39 String LOGIN_ACTION 动作:获取登录凭证。  
40 String MAIN_ACTION 动作:作为主入口点启动,不需要数据。  
41 String MEDIABUTTON_ACTION 广播:用户按下了“Media Button”。  
42 String MEDIA_BAD_REMOVAL_ACTION 广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)。  
43 String MEDIA_EJECT_ACTION 广播:用户想要移除扩展介质(拔掉扩展卡)。  
44 String MEDIA_MOUNTED_ACTION 广播:扩展介质被插入,而且已经被挂载。  
45 String MEDIA_REMOVED_ACTION 广播:扩展介质被移除。  
46 String MEDIA_SCANNER_FINISHED_ACTION 广播:已经扫描完介质的一个目录。  
47 String MEDIA_SCANNER_STARTED_ACTION 广播:开始扫描介质的一个目录。  
48 String MEDIA_SHARED_ACTION 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。  
49 String MEDIA_UNMOUNTED_ACTION 广播:扩展介质存在,但是还没有被挂载 (mount)。  
50 String MESSAGE_WAITING_STATE_CHANGED_ACTION 广播:电话的消息等待(语音邮件)状态已经改变。  
51 int MULTIPLE_TASK_LAUNCH 启动标记:和 NEW_TASK_LAUNCH 联合使用,禁止将已有的任务改变为前景任务 (foreground)。  
52 String NETWORK_TICKLE_RECEIVED_ACTION 广播:设备收到了新的网络 "tickle" 通知。  
53 int NEW_TASK_LAUNCH 启动标记:设置以后,activity 将成为历史堆栈中的第一个新任务(栈顶)。  
54 int NO_HISTORY_LAUNCH 启动标记:设置以后,新的 activity 不会被保存在历史堆栈中。  
55 String PACKAGE_ADDED_ACTION 广播:设备上新安装了一个应用程序包。  
56 String PACKAGE_REMOVED_ACTION 广播:设备上删除了一个应用程序包。  
57 String PHONE_STATE_CHANGED_ACTION 广播:电话状态已经改变。  
58 String PICK_ACTION 动作:从数据中选择一个项目 (item),将被选中的项目返回。  
59 String PICK_ACTIVITY_ACTION 动作:选择一个 activity,返回被选择的 activity 的类(名)。  
60 String PREFERENCE_CATEGORY 类别:activity是一个设置面板 (preference panel)。  
61 String PROVIDER_CHANGED_ACTION 广播:更新将要(真正)被安装。  
62 String PROVISIONING_CHECK_ACTION 广播:要求 polling of provisioning service 下载最新的设置。  
63 String RUN_ACTION 动作:运行数据(指定的应用),无论它(应用)是什么。  
64 String SAMPLE_CODE_CATEGORY 类别:To be used as an sample code example (not part of the normal user experience).  
65 String SCREEN_OFF_ACTION 广播:屏幕被关闭。  
66 String SCREEN_ON_ACTION 广播:屏幕已经被打开。  
67 String SELECTED_ALTERNATIVE_CATEGORY 类别:对于被用户选中的数据,activity 是它的一个可选操作。  
68 String SENDTO_ACTION 动作:向 data 指定的接收者发送一个消息。  
69 String SERVICE_STATE_CHANGED_ACTION 广播:电话服务的状态已经改变。  
70 String SETTINGS_ACTION 动作:显示系统设置。输入:无。  
71 String SIGNAL_STRENGTH_CHANGED_ACTION 广播:电话的信号强度已经改变。  
72 int SINGLE_TOP_LAUNCH 启动标记:设置以后,如果 activity 已经启动,而且位于历史堆栈的顶端,将不再启动(不重新启动) activity。  
73 String STATISTICS_REPORT_ACTION 广播:要求 receivers 报告自己的统计信息。  
74 String STATISTICS_STATE_CHANGED_ACTION 广播:统计信息服务的状态已经改变。  
75 String SYNC_ACTION 动作:执行数据同步。  
76 String TAB_CATEGORY 类别:这个 activity 应该在 TabActivity 中作为一个 tab 使用。  
77 String TEMPLATE_EXTRA 附加数据:新记录的初始化模板。  
78 String TEST_CATEGORY 类别:作为测试目的使用,不是正常的用户体验的一部分。  
79 String TIMEZONE_CHANGED_ACTION 广播:时区已经改变。  
80 String TIME_CHANGED_ACTION 广播:时间已经改变(重新设置)。  
81 String TIME_TICK_ACTION 广播:当前时间已经变化(正常的时间流逝)。  
82 String UMS_CONNECTED_ACTION 广播:设备进入 USB 大容量存储模式。  
83 String UMS_DISCONNECTED_ACTION 广播:设备从 USB 大容量存储模式退出。  
84 String UNIT_TEST_CATEGORY 类别:应该被用作单元测试(通过 test harness 运行)。  
85 String VIEW_ACTION 动作:向用户显示数据。  
86 String WALLPAPER_CATEGORY 类别:这个 activity 能过为设备设置墙纸。  
87 String WALLPAPER_CHANGED_ACTION 广播:系统的墙纸已经改变。  
88 String WALLPAPER_SETTINGS_ACTION 动作:显示选择墙纸的设置界面。输入:无。  
89 String WEB_SEARCH_ACTION 动作:执行 web 搜索。  
90 String XMPP_CONNECTED_ACTION 广播:XMPP 连接已经被建立。  
91 String XMPP_DISCONNECTED_ACTION 广播:XMPP 连接已经被断开。  

2、自定义广播

用户自定义的广播,对应自定义的广播接收器

设置感兴趣的ACTION即可

3、有序广播

(1)有序广播,必然就有优先级,广播的优先级为一个整数,范围为-1000~~~+1000 

静态注册往往比动态注册的优先级别要高

声明广播接收器的级别:android:priority="100"

(2)有序广播发送 为 :

Context.sendOrderedBroadcast(Intent)           发送普通的有序广播

(3)BroadcastReceiver.abortBroadcast() 中断广播,使级别低的广播接收器不能接收此广播

比如三个不同的广播接收器都可以接收同一个广播,设三个广播的优先级为-100,0,100,则一个广播发过来之后,优先级为100的广播接收器先接受,然后优先级0的广播接收器接收,

最后优先级为-100的广播接收器再接受,但是当优先级为100的广播接收器的onReceive()方法内部执行了abortBroadcast()方法,则广播被中断。后两个广播接收器都无法再接受此广播了

4、粘性广播(查了下子类,粘性广播已被放弃,过时)

粘性广播的特点为:

会一直保留到广播事件结束,没有所谓的10秒限制

但是当接收此广播的广播接收器的onReceive()方法执行过长时,系统会将此粘性广播设为可以干掉的candidate,意味着一旦系统资源不足,将会中断此广播

1  发送粘性的广播的方法 sendStickyBroadcast()

----------还有权限,不理解,既然不推荐使用了,就不深究了-----

5、本地广播(又:应用程序内部广播。重点,经常使用)

为什么这是重点呢? 由广播机制带来的安全性考虑

在清单文件中注册广播接收器的时候有一个属性

android:exported="true|false"

这里就需要注意了:

这个属性的意思是本应用程序是否允许接受外部应用程序发来的广播 ,像这样的属性,我们一般都会认为它的默认值为false

但是实际上 android:exported 的默认值是不确定的,怎么说,就是它的默认值是取决于 <intent-filter></intent-filter>存不存在的

第一种情况,<intent-filter></intent-filter>存在,则 android:exported的默认值为true

1       <receiver android:name="com.xqx.mybroadcase">
2             <intent-filter android:priority="80" >
3                 <action android:name="com.xqx.broadcast.xxx" />
4             </intent-filter>
5       </receiver>

第二种情况,<intent-filter></intent-filter>不存在,则 android:exported的默认值为false

1       <receiver android:name="com.xqx.mybroadcase"></receiver>

 当然activity和service 的该属性 也遵循同样的规则

--------------------------------------------------------------------------------------------------------------------------

综上,本地广播

就是为了解决该属性可能带来的安全隐患(其他APP恶意无限发送与intent-filter匹配的广播 或 其他APP也凑巧设置和同样的intent-filter )

本地广播相当于一个局部广播,发送广播的和接受广播的都属于同一个应用程序

对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(还需要ACTION匹配),

静态注册或其他方式动态注册的ContextReceiver接收不到。

使用方法:

      //注册应用内广播接收器
        LocalBroadcastManager  localBroadcastManager= LocalBroadcastManager.getInstance(this);
        localBroadcastManager.registerReceiver(receiver, intentFilter);
                
        //unregisterReceiver(receiver);
        //取消注册应用内广播接收器
        //localBroadcastManager.unregisterReceiver(receiver);

        Intent intent = new Intent();
        intent.setAction("com.xqx.broadcast.xxx");
        intent.putExtra("key", "hello world!");
        //sendBroadcast(intent);
        //发送应用内广播
        localBroadcastManager.sendBroadcast(intent);

补充:

解决广播安全性的方法:

1、发送广播时,可以设置广播的权限:sendBroadcast(Intent,String) 第二个参数为权限,为一个字符串

                     可以设置指定接收广播的应用程序的包:Intent.setPackage("com.xqx.app")

2、接收广播时 ,设置可以接收的广播的权限,Context.registBroadcast(Intent,String), 第二个参数String为接收广播的权限

        设置不接受外部应用程序的广播:<receiver androd:exported="false" ..>不接收外部应用的广播 

3、使用本地广播

-----------------------------------------------------------------------------------------------------------------------------

具体Demo就不写了,使用起来还是挺简单的

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

Android Studio最全插件整理

在Android开发中,合理的使用Android Studio插件不但可以提高开发效率,还能从整体上提高代码的质量。下面就Android开发中常见的一些插件做一...

3894
来自专栏非著名程序员

Activity 的4种启动模式,分析的简单全面

今天想来说说Android的启动模式,一来自己做一个总结,二来如果能帮助到别人就更好了~ 首先来看一个实际的业务场景。我之前在公司实习的时候,我所在的部门只负责...

2045
来自专栏贾鹏辉的技术专栏@CrazyCodeBoy

FileProvider无法获取外置SD卡问题解决方案 | Failed to find configured root that contains

尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://www.devio.org) 随着Android版本越来越高,Android官方对...

6068
来自专栏FreeBuf

命令行下的“蒙面歌王”rundll32.exe

*本文原创作者:lcx,本文属FreeBuf原创奖励计划,未经许可禁止转载 ** 在Windows系统中,为了节省内存和实现代码重用,微软在Windows操作系...

2549
来自专栏向治洪

android离线缓存技术

离线缓存是指在有网络的状态下将从服务器获取的网络数据,如Json 数据缓存到本地,在断网的状态下启动APP时读取本地缓存数据显示在界面上,常用的APP(网易新...

4099
来自专栏hbbliyong

Android 蓝牙操作详解

1.启用蓝牙并使设备处于可发现状态        1.1 在使用BluetoothAdapter类的实例进操作之前,应启用isEnable()方法检查设备是否启...

4379
来自专栏做全栈攻城狮

安卓项目-利用Sqlite数据库,开发新闻发布系统

本文章是基于上篇文章基础之上进行深入学习的。程序员带你学习安卓开发-XML文档的创建与解析

1824
来自专栏非著名程序员

一键清理应用数据或者清除应用缓存的方法

导语:最近在做一个一键清理应用缓存的功能,做着做着发现挺有意思,总结了两种方法,供大家参考。 ? 一种是退出应用时,清除应用里的缓存数据。这种方法跟在设置里的...

2919
来自专栏Android中高级开发

首个hybird商业项目踩坑总结

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

1311
来自专栏Sorrower的专栏

Chronometer和CountDownTimer计时器

Android小知识10则(上) Android小知识10则(下) Android用5种方式实现自定义计时器, 哪种才是你的菜? github传送门

883

扫码关注云+社区

领取腾讯云代金券