1. Android 中的广播可以分为两种类型:标准广播和有序广播
- 标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
- 有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够接收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级搞得广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法接收到广播消息了。
2. 接收系统广播
Android 内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。
2.1 动态注册监听网络变化
需要添加权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
代码:
public class BroadcastTestActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast_test);
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) {
ConnectivityManager connectivityManager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
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();
}
}
}
}
- 首先定义了一个内部类NetworkChangeReceiver,这个类是继承自BroadcastReceiver 的,并重写了父类的 onReceive() 方法,这里做接收到广播以后的处理。
- 然后在 onCreate() 方法里面创建了一个IntentFilter的实例,并给他添加了一个 action,这个 action 就算系统网络变化时发出的广播。
- 接下来创建了一个 NetworkChangeReceiver 的实例,然后调用 registerReceiver() 方法进行注册,将 NetworkChangeReceiver 的实例和 IntentFilter 的实例都传了进去,这样 NetworkChangeReceiver 就会接收到 上面 action 的广播,也就实现了监听网络变化。
- 最后记得,动态注册的广播接收器一定要取消注册才行,这里我们是在 onDestroy() 方法中通过调用 unregisterReceiver() 方法来实现的。
- 再说一下 onReceive() 方法,这里首先通过 getSystemService() 方法得到了 ConnectivityManager 的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的 getActivityNetworkInfo() 方法可以得到 NetworkInfo 的实例,接着调用 NetworkInfo 的 isAvailable() 方法,就可以判断当前是否有网络了,最后通过 Toast 展示。
2.2 静态注册实现开机启动
动态注册必须在程序启动之后才能接收到广播,因为注册的逻辑写在 onCreate() 方法中,想让程序在未启动的情况下就能接收到广播,就需要静态注册。
- 创建广播接收器
Exported 属性表示是否允许这个广播接收器接收本程序以外的广播,Enabled 属性表示是否启用这个广播接收器。
- 修改 接收器 中的代码
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public 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();;
}
}
- 在 AndroidManifest.xml 文件中注册(AS已经帮助完成)
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
</receiver>
- 添加权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
- 为广播接收器注册监听action
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
* 注意:不要再 onReceive() 方法中添加过多的逻辑或者耗时操作,因为在广播接收器中是不允许开启线程的,当 onReceive() 方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打成程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等。 *
3. 发送自定义广播
3.1 发送标准广播
- 新建一个 MyBroadcastReceiver
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"receive in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();;
}
}
- 在 AndroidManifest.xml 中对这个广播接收器进行修改
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.test.MY_BROADCAST"/>
</intent-filter>
</receiver>
- MainActivity 里发送这条广播
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast_test);
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.test.MY_BROADCAST");
sendBroadcast(intent);
}
});
}
3.2 发送有序广播
- 发送有序广播
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.test.MY_BROADCAST");
sendOrderedBroadcast(intent,null);
}
});
发送有序广播只需要将 sendBroadcast() 方法改成 sendOrderedBroadcast() 方法。sendOrderedBroadcast() 方法接收两个参数,第一个参数仍然是 Intent ,第二个参数是一个与权限相关的字符串,这里传入null就行。
- 设置 广播接收器 优先级
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.test.MY_BROADCAST"/>
</intent-filter>
</receiver>
通过 android:priority 属性给广播接收器设置优先级,优先级比较高的广播接收器就可以先收到广播。
- 截断广播
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"receive in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();;
abortBroadcast();
}
在 onReceive() 方法中调用了 abortBroadcast() 方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。
4. 使用本地广播
前面我们发送和接收的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样很容易引起安全性的问题,比如我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
为了简单地解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
本地广播主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
public class BroadcastTestActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast_test);
localBroadcastManager = LocalBroadcastManager.getInstance(this);//获取实例
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.test.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);//发送本地广播
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.test.LOCAL_BROADCAST");
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,"receive local broadcast",Toast.LENGTH_SHORT).show();
}
}
}
这里基本上和前面的动态注册广播接收器以及发送广播的代码是一样的,只不过首先是通过 LocalBroadcastManager 的 getInstance() 方法得到了它的一个实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager 的 registerReceiver() 方法,在发送广播的时候调用的是 LocalBroadcastManager 的 sendBroadcast() 方法。
* 注意:本地广播无法通过静态的方式来接受。 *
使用本地广播的几点优势
1)发送的广播不会离开我们的程序,不必担心机密数据泄露。
2)其他的程序无法将广播发送到我们程序的内部,不需要担心会有安全漏洞的隐患。
3)发送本地广播比发送系统全局广播会更加高效。