一,广播的分类
1,标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
2,有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。
二,广播的注册方式
注册广播的方式一般有两种,在代码中注册和在AndroidManifest.xml 中注册,其中前者也被称为动态注册,后者也被称为静态注册。
那么该如何创建一个广播接收器呢?其实只需要新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。
1,动态注册
registerReceiver(networkChangeReceiver, intentFilter);和unregisterReceiver(networkChangeReceiver);
public class MainActivity extends Activity {
private IntentFilter intentFilter;
private 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();
}
}
}
观察onCreate()方法,首先我们创建了一个IntentFilter 的实例,并给它添加了一个值为android.net.conn.CONNECTIVITY_CHANGE 的action,为什么要添加这个值呢?因为
当网络状态发生变化时, 系统发出的正是一条值为android.net.conn.CONNECTIVITY_CHANGE 的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action 就行了。
最后要记得,动态注册的广播接收器一定都要取消注册才行,这里我们是在onDestroy()方法中通过调用unregisterReceiver()方法来实现的。
2,静态注册 可实现开机启动
动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在
onCreate()方法中的。那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢?这就需要使用静态注册的方式了。
<receiver
android:name="com.hjt.receiver.BootCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
在<application>标签内出现了一个新的标签<receiver>,所有静态注册的广播接收器都是在这里进行注册的。它的用法其实和<activity>标签非常相似,首先通过android:name来指定具体注册哪一个广播接收器,然后在<intent-filter>标签里加入想要接收的广播就行了,由于Android 系统启动完成后会发出一条值为android.intent.action.BOOT_COMPLETED 的广播,因此我们在这里添加了相应的action。
注意:对于静态注册不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml 中将这个广播接收器的类名注册进去。
到目前为止,我们在广播接收器的onReceive()方法中都只是简单地使用Toast 提示了一段文本信息,当你真正在项目中使用到它的时候,就可以在里面编写自己的逻辑。需要注意的是,不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等。
三,发送自定义广播
在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发出去也是白发。
广播是一种可以跨进程的通信方式,这一点从前面接收系统广播的时候就可以看出来了。因此在我们应用程序内发出的广播,其他的应用程序应该也是可以收到的。
1,发送标准广播:sendBroadcast()
2,发送有序广播:sendOrderedBroadcast()
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//发送自定义的(有序)广播com.example.broadcasttest.MY_BROADCAST
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendOrderedBroadcast(intent, null);
}
});
发送有序广播只需要改动一行代码, 即将sendBroadcast() 方法改成sendOrderedBroadcast()方法。sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入null 就行了。
那么该如何设定广播接收器的先后顺序呢?当然是在注册的时候进行设定的了,修改AndroidManifest.xml 中的代码
<receiver android:name=".MyBroadcastReceiver">
<intent-filter android:priority="100" >
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
可以看到,我们通过android:priority 属性给广播接收器设置了优先级,优先级比较高的广播接收器就可以先收到广播。这里将MyBroadcastReceiver 的优先级设成了100,以保证它一定会在AnotherBroadcastReceiver 之前收到广播。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceive",Toast.LENGTH_SHORT).show();
//截断广播
abortBroadcast();
}
}
如果在onReceive()方法中调用了abortBroadcast()方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。
四,本地广播(为了安全考虑)
前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
为了能够简单地解决广播的安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。本地广播的用法并不复杂,主要就是使用了一个LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
public class MainActivity extends Activity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 获取实例
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcasttest.
LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent); // 发送本地广播
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.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, "received local broadcast",
Toast.LENGTH_SHORT).show();
}
}
}
另外还有一点需要说明,本地广播是无法通过静态注册的方式来接收的。其实这也完全可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。最后我们再来盘点一下使用本地广播的几点优势吧。
1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。
2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。
3. 发送本地广播比起发送系统全局广播将会更加高效。