作者Talent•C
转载请注明出处
前言
端午节后,第一天上班,我们也来学一点新知识吧,Android
中的四大组件之二 ——— Broadcast Receiver(广播)。
Broadcast Receiver 也就是 “广播接收者” 的意思,顾名思义,它就是用来接收来自系统和应用中的广播。在Android
系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。Android
中的广播机制设计的非常出色,很多事情原本需要开发者亲自操作的,现在只需等待广播告知自己就可以了,大大减少了开发的工作量和开发周期。而作为应用开发者,就需要数练掌握Android
系统提供的一个开发利器,那就是 Broadcast Receiver。
Android
中有哪些广播?
在Android
中,广播可以分为两大类:标准广播(Normal broadcasts) 和 有序广播(Ordered broadcasts)。
标准广播: 一种异步执行的广播,在广播发出后所有的接收者几乎是同一时间接收到广播。这种广播效率很高,但是没有先后顺序所以一位置不能被截断。
有序广播: 一种同步执行的广播,在广播发出后优先级最高的广播接收器可以接收到这条广播,当这个广播接收器中的逻辑执行完成以后,广播才会继续传递。因此如果在优先级高的广播接收器中截断了正在传递的广播,那么后面的广播接收器就无法接收到这条广播。
接收广播的基本步骤:
我们可以接收自定义的广播,也可以接收系统的广播。系统会发出很多广播,例如开机启动完成,电量过低等都会发出广播,但是想接收这些广播需要声明权限。
监听系统广播
Android
内置了很多系统级别的广播,我们可以在应用程序中监听这些广播获得系统的状态信息,如监听系统网络状态变化等。
Android
中注册监听有两种方式: 动态注册监听 和 静态注册监听。
动态注册监听: 在代码中进行注册后,当应用程序关闭后,就不再进行监听。
静态注册监听: 在 AndroidManifest.xml 文件中注册。不管改应用程序是否处于活动状态,都会进行监听,比如某个程序时监听内存 的使用情况的,当在手机上安装好后,不管改应用程序是处于什么状态,都会执行改监听方法中的内容。
监听系统广播之 —— 动态注册监听
刚才我们提到如果程序需要使用用户比较敏感的操作,就必须在配置文件中声明权限才可以,否则程序直接崩溃掉。
因为我们需要监控设备的网络状态变化,所以我们需要在配置文件中添加权限。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.broadcasttest.chuliangliang.broadcasttest"> //声明权限 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
|
自定义一个类 NetworkChangeReveiver 继承自 BroadcastReceiver,重写 onReceive()
方法,并添加自己的代码逻辑,收到广播时,就会调用 onReceive()
方法。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class NetworkChangeReveiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); Log.d("NetworkStateBroadcast", "onReceive: " + networkInfo); if (networkInfo != null && networkInfo.isAvailable()) { Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(context, "网络断开连接", Toast.LENGTH_SHORT).show(); } } }
|
广播监听类创建完毕,我们开始注册广播监听,修改 MainActivity 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MainActivity extends AppCompatActivity { private NetworkChangeReveiver networkStateBroadcast; private IntentFilter intentFilter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkStateBroadcast = new NetworkChangeReveiver(); registerReceiver(networkStateBroadcast,intentFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(networkStateBroadcast); } }
|
上述代码中,初始换广播监听类,向系统中注册监听,并指定监听的类型为网络状态变化,到网络状态发生改变时就会调用 NetworkChangeReveiver 的 onReceive()
方法,我们就可以在这个方法中做一些事情。最后要记得动态注册的广播接收器一定要取消注册,这里我们在活动的onDestroy()
方法中取消注册。
动态注册广播接收器可以很自由的控制注册与注销,使用起来非常灵活,但是存在一个缺点,必须在程序启动之后才可以接收到广播,因为注册广播接收器的逻辑写在onCreate()
方法中。而静态注册广播接收器则可以在程序未启动的情况下接收到广播。
监听系统广播之 —— 静态注册监听
我们这次来监听一条开机广播,从而实现开机启动的功能。我们可以使用Android Studio
提供的快捷方式来创建一个广播接收器,在 包名的文件夹下(存放java源码的文件夹)右键 –> New –> Other –> Broadcast Receiver,就会弹出一个对话框,我们可以输入将要创建的广播类名 BootCompleteReceiver, 在输入类名的下方有两个复选框, 第一个复选框 Exported 表示是否允许这个广播接收器接收本程序之外的广播,第二个复选框 Enabled 表示是否启用这个广播接收器,我们这里将两个都选中,点击 finish 完成创建。
打开新创建的广播接收器,代码如下:
1 2 3 4 5 6
| public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Boot complete", Toast.LENGTH_SHORT).show(); } }
|
这与我刚才手动创建的广播接收器类的代码完全一致,下一步我们需要在配置文件中注册这个广播接收器,我们打开 AndroidManifest.xml,注册广播接收器,由于我们是使用快捷方式创建,所以系统会自动帮我们处理了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.broadcasttest.chuliangliang.broadcasttest"> //声明权限 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //静态注册广播接收器 <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> //指定这个广播接收器 接收的广播类型 <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </manifest>
|
我们修改上面的代码,设置广播接收器接收的广播类型为 android.intent.action.BOOT_COMPLETED。同样,也需要我们声明权限 android.permission.RECEIVE_BOOT_COMPLETED。
运行程序后,将手机或者模拟器关闭,重新开机,我们的程序就会监听到系统开机的广播并弹出提示。
自定义广播的发送与监听
前面我们说过广播分为两种: 标准广播 和 有序广播。现在我们就分别来看一下如何实现。
自定义广播之 —— 标准广播
我们在 MainActivity 中添加一个按钮,点击这个按钮,发送一条广播,在广播中我们可以携带参数,然后在广播接收器中通过 Toast 提示给用户。
1)定义广播接收器与上面的方式相同,我们取名 MyBroadcastReceiver, 创建步骤就不再重复了,代码如下:
1 2 3 4 5 6
| public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "MyBroadcastReceiver 接收到广播" + intent.getStringExtra("data_key"), Toast.LENGTH_SHORT).show(); } }
|
2)修改 AndroidManifest.xml 文件注册广播接收器, 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.broadcasttest.chuliangliang.broadcasttest"> //声明权限 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //静态注册广播接收器 <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> //指定这个广播接收器 接收的广播类型 <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> //注册自定义广播的 接收器 并制定广播名字 com.buuuu.MYBROADCAST <receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.buuuu.MYBROADCAST" /> </intent-filter> </receiver> </manifest>
|
上面两个步骤都是我们之前介绍过的, 我们通过上面步骤完成了广播接收器的定义及注册,下面就让我们来看看,如何发放广播?
3)发送广播,修改按钮点击事件,在点击事件中发送一条广播。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button)findViewById(R.id.button_1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction("com.buuuu.MYBROADCAST"); intent.putExtra("data_key","我是广播数据"); sendBroadcast(intent); } }); } }
|
运行程序,点击按钮,会在屏幕上弹出一句文字提示,同时会将广播中传递的字符串显示出来,这就是Android
中标准广播的发送与接收。
自定义广播之 —— 有序广播
我们只需要将上述代码中的 sendBroadcast(intent); 修改为 sendOrderBroadcast(intent,null); 即可,sendOrderBroadcast() 方法需要传两个参数,第一个参数仍然是Intent
,第二个参数是一个与权限相关的字符串,这里使用null即可。
我们再创建一个广播接收器 MyBroadcastReceiver02,为了省事我们将 MyBroadcastReceiver 复制一份,修改名字为 MyBroadcastReceiver02。 那么问题来了,如何指定两个广播接收器的接收顺序?我们可以通过设置广播接收器的优先级来指定广播接收器的接收顺序;设置广告接收器优先级使用如下方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.broadcasttest.chuliangliang.broadcasttest"> //声明权限 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //静态注册广播接收器 <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> //指定这个广播接收器 接收的广播类型 <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> //注册自定义广播的 接收器 并制定广播名字 com.buuuu.MYBROADCAST <receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="200"> <action android:name="com.buuuu.MYBROADCAST" /> </intent-filter> </receiver> // 注册自定义广播的 接收器002 并制定广播名字 com.buuuu.MYBROADCAST <receiver android:name=".MyBroadcastReceiver002" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.buuuu.MYBROADCAST" /> </intent-filter> </receiver> </manifest>
|
通过 android:priority=”200” 为广播接收器设置优先级,优先级越高,越先监听到广播消息。运行程序,我们可以看到 MyBroadcastReceiver 第一个接收到广播消息,之后 MyBroadcastReceiver002 接收到广播消息。我们也可以在 MyBroadcastReceiver 监听到广播消息后手动中断广播不再继续传播,修改 MyBroadcastReceiver 中的代码如下:
1 2 3 4 5 6 7
| public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "MyBroadcastReceiver 接收到广播" + intent.getStringExtra("data_key"), Toast.LENGTH_SHORT).show(); abortBroadcast(); } }
|
我们在 MyBroadcastReceiver 广播接收器onReceive()
方法中接收到广播消息后处理消息并调用abortBroadcast()
方法中断广播消息接续传递,也就是 MyBroadcastReceiver 可以收到广播 MyBroadcastReceiver002 则不能收到广播消息了。
运行程序,测试一下,点击按钮发送广播消息, MyBroadcastReceiver 可以监听到而 MyBroadcastReceiver002 则不能收到消息。
本地广播机制
前面我们发送的广播都是属于系统全局广播,即发出的广播可以被其他程序监听到,并且我们也可以接收来自其他程序的广播,这样就很容易引起安全性的问题,例如我们发送的广播中带有一些很重要的数据被其他程序监听到,或者其他程序不停向我们的程序发送垃圾广播等。
为了能有效避免这个问题,Android
中引入了一套本地广播机制,使用这种机制的广播只能在程序内部进行传递,并只能接受来自程序内部的广播。
本地广播的使用很简单,主要使用了 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.broadcasttest.chuliangliang.broadcasttest; import android.content.Intent; import android.content.IntentFilter; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class Main2Activity extends AppCompatActivity { private IntentFilter intentFilter; private MyReceiver myReceiver; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); localBroadcastManager = LocalBroadcastManager.getInstance(this); intentFilter = new IntentFilter(); intentFilter.addAction("com.custom.MYBROADCAST"); myReceiver = new MyReceiver(); localBroadcastManager.registerReceiver(myReceiver,intentFilter); Button button = (Button)findViewById(R.id.button_2); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction("com.custom.MYBROADCAST"); intent.putExtra("data_key","我是广播数据2"); localBroadcastManager.sendBroadcast(intent); } }); } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(myReceiver); } }
|
通过代码可以看出,本地广播与之前介绍的系统全局广播的使用上都是一样的,唯一的区别就是使用 LocalBroadcastManager 去注册/发送/注销。
注意: 本地广播是无法通过静态注册的方式来接收的。
总结
今天的内容相对来说比较少,也很好理解,学习过iOS
的小伙伴一定会发现,Android
中的广播与iOS
中的通知非常相似。广播接收器是Android
中四大组件之一,在不知不觉中我们已经掌握了四大组件中的两个了。