文章目录
- 广播概念
- 接收广播
- 动态注册
- 实例
- 静态注册
- 实例
- 发送广播
- 发送标准广播
- 广播的跨进程特性
- 发送有序广播
- 本地广播
广播概念
Android 中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自系统的,也可能是来自于其他应用程序的。
广播有两种类型——有序广播和标准广播:
- 标准广播: 一种完全异步执行的广播,在广播发出去之后,所有的广播接收器几乎都会同一时刻接收到这条广播消息,没有任何的先后顺序可言,这种广播的效率比较高,但是无法被截断。
- 有序广播: 是一种同步执行的广播,在广播发出去之后,同一时刻只会有一个广播接收器能够收到这条消息,当这个广播接收器中的逻辑执行完毕之后,广播才会继续传递,所以这时候的广播接收器是有优先级顺序的,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播就无法收到广播消息。
接收广播
动态注册
Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。比如手机开机、电池电量发生变化、时间或者时区发生改变等等。如果想要接收到这些广播就需要使用广播接收器。
注册广播的方式一般也有两种,在 代码中注册(动态注册) 或者 在 AndroidManifest.xml
中注册(静态注册)。
实例
我们实现一个能准确地告诉用户当前有没有网络的功能,在实现代码前,由于 Android 为了保护用户设备的隐私和安全,规定了程序需要进行一些对用户来说比较敏感的操作,必须在配置文件中声明权限才可以,该功能监听了网络的变化,所以必须在 AndroidManifest.xml
配置权限才能访问系统网络状态:
public class BroadcastActivity extends AppCompatActivity {private IntentFilter intentFilter; // 意图过滤器,旨在匹配可以响应对应操作的组件private NetworkChangeReceiver networkChangeReceiver; // 自定义的广播接收器类@Overrideprotected 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{@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void onReceive(Context context, Intent intent) {// 收到自定义广播时会弹出提示Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_LONG).show();}
}
在 AndroidManifest.xml
中注册广播的值:
实现点击 BroadcastActivity
活动中的 send broadcast
按钮来发送广播:
布局文件 broad_layout.xml
:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid: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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.broad_layout);Button button = findViewById(R.id.button_broadcast1);button.setOnClickListener((View v)->{// 将要发送的广播植入IntentIntent 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";@Overridepublic 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 {@Overridepublic 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")@Overrideprotected 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");// 将要发送的广播植入IntentIntent 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);}// 动态注册一定要在结束时取消注册@Overrideprotected void onDestroy() {super.onDestroy();localBroadcastManager.unregisterReceiver(localReceiver);}class LocalReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "本地广播", Toast.LENGTH_LONG).show();}}
}