Android四大组件之Broadcast
Android四大组件之Broadcast。
Broadcast虽然是四大组件,但是感觉用到的挺少的,感觉项目中我只用到了判断网络状态和判断电量
下面整理资料,主要来源于郭霖的《第一行代码》第二版和网络
广播分为
1.标准广播
是一种完全异步执行的广播,在广播发出去的之后,所有的广播接收器在同一时间接收到了广播,因此他们之间没有任何先后顺序可言,这种广播的效率会比较高,但是同时一位置他无法被截断。工作流程如下图所示
2.有序广播
是一种同步执行的广播,在广播发出去以后,同一时间只有一个广播接收器可以接收到此条广播,当这个广播接收器中的判断逻辑走完之后,广播才会继续传播,所以此时的广播接收器是有先后顺序的,优先级高的广播接收器会先收到广播,并且前面的广播接收器还能拦截后边的广播接收器,这样后边的广播接收器就无法收到广播消息了,工作流程如图所示
了解了广播的基本分类,有序广播,无序广播之后,我们来进行一些简单的demo
接收系统广播
android内置很多系统级别的广播,我们可以在应用中通过监听这些广播来得到各种系统的状态信息。比如手机开机后会发出一条广播,电池电量发生变化时候会发生广播,时间和时区发生变化的时候会发出一条广播,等等。如果想要接收到这些广播,就需要使用广播接收器,
具体系统广播可检测到什么,见附件1。
注册广播一般分为:
在代码中注册:动态注册
在清单配置文件中注册,静态注册
1.动态注册监听网络变化
新建一个内部类,让他继承BroadcastReceiver,并重写父类的onReceiver()方法
这样当广播到来时,onReceover()方法就会执行
具体的逻辑就在这个方法中进行
public class MainActivity extends AppCompatActivity { IntentFilter intentFilter; NetWorkChangeReceiver netWorkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter=new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); netWorkChangeReceiver=new NetWorkChangeReceiver(); registerReceiver(netWorkChangeReceiver,intentFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(netWorkChangeReceiver); } class NetWorkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"network changes",Toast.LENGTH_SHORT).show(); } } }
效果图
动态注册的关闭,需要取消注册,这里,我们实在onDestory()中调用unregisterReceiver()来实现
2.静态注册实现开机启动
动态注册的广播接收器可以*的控制注册和注销,在灵活性方面有很大的优势,但是他存在一个缺点,即必须在程序启动之后才能接收到广播,因为注册的逻辑卸载oncreate()中,
如果想要在程序未启动的情况下就能接收到广播,那就需要静态注册
这里我们准备让程序接收一条开机关闭,用快捷方式在as中注册广播
创建广播BootCompleteReceiver
public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show(); } }
可以在配置文件中找到静态注册的广播
我们加入interFilter标签,并且添加先用action
当然需要添加权限
到目前为止,我们在广播接收器onReceiver()方法中都只是简单的用toast提示了一段文本信息,当你真正的在项目中用到它的时候,就可能在里边编写自己的逻辑。需要注意的事,不要再receiver()方法中添加过多的逻辑,或者进行任何的好事操作,因为在广播接收器中是不允许开线程的,当onReceiver方法运行了较长时间而没有结束的时候,程序就会报错,因此广播接收器更多的实质扮演一种打程序其他组件的角色,比如创建一个状态栏通知,或者开启一个服务,
3.发送自定义广播
3.1发送标准广播
首先创建一个广播接收器
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"发送自定义本地标准广播",Toast.LENGTH_SHORT).show(); } }
在配置文件中设置这个广播接收器
可以看到让MyBroadcastReceiver接收一个广播,值为“fxr”。我们需要发出一个值为
fxr的广播
Activity中的代码
public class SecondActivity extends AppCompatActivity { Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); button=(Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent("fxr"); sendBroadcast(intent); } }); } }
3.2发送有序广播‘
广播是一种跨进程的通信方式,这一点从前面接收系统广播的时候我们就可以看出来,因此我们app发出的广播,其他app也可以就收到,为了验证这一点,我们需要在创建一个项目broadcasetest2
然后直接创建一个广播
public class AnotherBroadcaseReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"antoher",Toast.LENGTH_SHORT).show(); } }
同样的是接收“fxr”
这时候我们运行下这个demo2,然后运行下demo1,此时保证demo2在后台存在
我们点击demo的按钮,就会弹出来2个toast
这就证明,我们程序发出来的广播,别的程序也能接收到,
不过,目前为止,我们都是发送的标准广播,我们尝试下发送有序广播,修改BroadcastTest项目(demo1)中的代码
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent("fxr"); sendOrderedBroadcast(intent,null); } });
可以看到,发送有序广播只需要改动一行代码,将sendBroadcast()方法改成sendOrderBroadcast()方法。
sendOrderBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个和权限相关的字符换,我们这里传入null就好,现在我们点击按钮,这就很尴尬,2个广播的接收器的toast还是都弹出,看上去和标准广播没什么区别,我们可以设置优先级,给广播接收器设置先后顺序,我们给demo2 的配置文件中进行修改
可以看到,我们通过android:priority属性给广播设置了优先级,优先级比较高的可以先接收到关闭,我们吧demo2设置为100,我们在运行测试下
可以先看到another在显示另外一个toast,这样就验证了广播的优先级
既然已经得到了广播的优先级,那么我们可以选择是否广播继续传递,修改代码
public class AnotherBroadcaseReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"antoher",Toast.LENGTH_SHORT).show(); abortBroadcast(); } }
调用abortBroadcast方法,就表示广播被截端,后边无法继续收到广播,测试结果和预期一样,美滋滋
4.使用本地广播
前面我们发送和接收的广播全部属于全局广播,发出的广播可以被其他应用所截获,为了解决广播的安全性,andorid引入了,本地广播机制,使用这个机制发出的广播只能在本app之中传奇,并且广播接收器也只能接收到来自本应用的广播
本地广播的用法并不复杂,主要就是使用一个localBroadcastManager,来对广播进行管理,并提供了发送广播和注册广播接收器的方法,下面我们来试下
public class SanActivity extends AppCompatActivity { private IntentFilter intentFilter; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_san); localBroadcastManager=LocalBroadcastManager.getInstance(this); Button button=(Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent("fxr2"); localBroadcastManager.sendBroadcast(intent); } }); intentFilter=new IntentFilter(); intentFilter.addAction("fxr2"); 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,"receiver local broadcast",Toast.LENGTH_SHORT).show(); } } }
通过localBroadcastManager的getInstance()方法得到实例,然后注册广播的时候调用LocalBroadcastManager的registerReceiver()方法,
点击按钮
可以看到,我们成功的接收到了本地广播,
另外还有一点需要说明,本地广播是无法进行静态注册的方式来接收的,
最后我们来盘点下使用本地广播的几点优势吧
1.快,是真的快,比全局的广播要快很多
2.可以明确的知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄漏。
3.真的是比全局广播要快,谁用谁知道
写不动了要睡觉了,明天再补充一个demo,然后找下网上有木有比较好用的广播技巧
附件
一.系统广播定义的一些action值展示:
1.Intent.ACTION_AIRPLANE_M; 关闭或打开飞行模式时的广播 2.Intent.ACTION_BATTERY_CH; (1)充电状态,或者电池的电量发生变化; (2)电池的充电状态、电荷级别改变,不能通过组建声; 3.Intent.ACTION_AIRPLANE_MODE_CHANGED; 关闭或打开飞行模式时的广播 4.Intent.ACTION_BATTERY_CHANGED; (1)充电状态,或者电池的电量发生变化 (2)电池的充电状态、电荷级别改变,不能通过组建声明 接收这个广播,只有通过registerReceiver()注册 5.Intent.ACTION_BATTERY_LOW; 表示电池电量低 6 .Intent.ACTION_BATTERY_OKAY; 表示电池电量充足,即从电池电量低变化到饱满时会发出广播 7.Intent.ACTION_BOOT_COMPLETED; 在系统启动完成后的广播,这个动作被广播一次(只有一次)。 8.Intent.ACTION_CAMERA_BUTTON; 按下照相时的拍照按键(硬件按键)时发出的广播 9.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; 当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,android系统都会广播此Action消息 10.Intent.ACTION_CONFIGURATION_CHANGED; 设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.java) 11.Intent.ACTION_DATE_CHANGED; 设备日期发生改变时会发出此广播 12.Intent.ACTION_DEVICE_STORAGE_LOW; 设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用 13.Intent.ACTION_DEVICE_STORAGE_OK; 设备内存从不足到充足时发出的广播,此广播 只能由系统使用,其它APP不可用 14.Intent.ACTION_DOCK_EVENT; 发出此广播的地方 frameworks\base\services\java\com\android\server\DockObserver.java 15.Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE; 移动APP完成之后,发出的广播(移动是指:APP2SD) 16.Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; 正在移动APP时,发出的广播(移动是指:APP2SD) 17. 17.Intent.ACTION_GTALK_SERVICE_CONNECTED; Gtalk已建立连接时发出的广播 18.Intent.ACTION_GTALK_SERVICE_DISCONNECTED; Gtalk已断开连接时发出的广播 19.Intent.ACTION_HEADSET_PLUG; 在耳机口上插入耳机时发出的广播 20.Intent.ACTION_INPUT_METHOD_CHANGED; 改变输入法时发出的广播 21.Intent.ACTION_LOCALE_CHANGED; 设备当前区域设置已更改时发出的广播 22.Intent.ACTION_MANAGE_PACKAGE_STORAGE; 表示用户和包管理所承认的低内存状态通知应该开始。 23. Intent.ACTION_MEDIA_BAD_REMOVAL; 未正确移除SD卡(正确移除SD卡的方法:设置–SD卡和设备内存–卸载SD卡),但已把SD卡取出来时发出的广播 ,扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mountpoint) 还没解除 (unmount) 24.Intent.ACTION_MEDIA_BUTTON; 按下”Media Button” 按键时发出的广播,假如有”Media Button” 按键的话(硬件按键) 25. Intent.ACTION_MEDIA_CHECKING; 插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播。 26.Intent.ACTION_MEDIA_EJECT; 已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播, 用户想要移除扩展介质(拔掉扩展卡)。 27.Intent.ACTION_MEDIA_MOUNTED; 插入SD卡并且已正确安装(识别)时发出的广播, 扩展介质被插入,而且已经被挂载。 28.Intent.ACTION_MEDIA_NOFS; 拓展介质存在,但使用不兼容FS(或为空)的路径安装点检查介质包含在Intent.mData领域。 29. Intent.ACTION_MEDIA_REMOVED; 外部储存设备已被移除,不管有没正确卸载,都会发出此广播, 扩展介质被移除。 30.Intent.ACTION_MEDIA_SCANNER_FINISHED; 广播:已经扫描完介质的一个目录 31.Intent.ACTION_MEDIA_SCANNER_SCAN_FILE; 请求媒体扫描仪扫描文件并将其添加到媒体数据库。 32.Intent.ACTION_MEDIA_SCANNER_STARTED; 广播:开始扫描介质的一个目录 33. Intent.ACTION_MEDIA_SHARED; 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。 34.Intent.ACTION_MEDIA_UNMOUNTABLE; 35.Intent.ACTION_MEDIA_UNMOUNTED 广播:扩展介质存在,但是还没有被挂载 (mount) 36.Intent.ACTION_NEW_OUTGOING_CALL; 37. Intent.ACTION_PACKAGE_ADDED; (1)成功的安装APK之后 (2)广播:设备上新安装了一个应用程序包。 (3)一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播) 38.Intent.ACTION_PACKAGE_CHANGED; 一个已存在的应用程序包已经改变,包括包名 39.Intent.ACTION_PACKAGE_DATA_CLEARED; (1)清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时?) (2)用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播) 40.Intent.ACTION_PACKAGE_INSTALL; 触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用? 41.Intent.ACTION_PACKAGE_REMOVED; 成功的删除某个APK之后发出的广播, 一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播) 42.Intent.ACTION_PACKAGE_REPLACED; 替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播?) 43.Intent.ACTION_PACKAGE_RESTARTED; 用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播) 44.Intent.ACTION_POWER_CONNECTED; 插上外部电源时发出的广播 45.Intent.ACTION_POWER_DISCONNECTED; 已断开外部电源连接时发出的广播 46.Intent.ACTION_PROVIDER_CHANGED; 47.Intent.ACTION_REBOOT; 重启设备时的广播 48.Intent.ACTION_SCREEN_OFF; 屏幕被关闭之后的广播 49. Intent.ACTION_SCREEN_ON; 屏幕被打开之后的广播 50.Intent.ACTION_SHUTDOWN; 关闭系统时发出的广播 51. Intent.ACTION_TIMEZONE_CHANGED; 时区发生改变时发出的广播 52.Intent.ACTION_TIME_CHANGED; 时间被设置时发出的广播 53.Intent.ACTION_TIME_TICK; 广播:当前时间已经变化(正常的时间流逝), 当前时间改变,每分钟都发送,不能通过组件声明来接收 ,只有通过Context.registerReceiver()方法来注册 54. Intent.ACTION_UID_REMOVED; 一个用户ID已经从系统中移除发出的广播 55. Intent.ACTION_UMS_CONNECTED; 设备已进入USB大容量储存状态时发出的广播? 56.Intent.ACTION_UMS_DISCONNECTED; 设备已从USB大容量储存状态转为正常状态时发出的广播? 57.Intent.ACTION_USER_PRESENT; 58.Intent.ACTION_WALLPAPER_CHANGED; 设备墙纸已改变时发出的广播