Android组件(三)Broadcast
注:本文代码部分参考《第一行代码 Android》第二版
1.简述Broadcast
Broadcast 是 Android 四大组件之一,是一种广泛运用在应用程序之间异步传输信息的机制。Broadcast 本质上是一个Intent 对象,差别在于 Broadcast 可以被多个 BroadcastReceiver 处理。BroadcastReceiver 是一个全局监听器,通过它的 onReceive() 可以过滤用户想要的广播,进而进行其它操作。广播的发送者和接收者事先是不需要知道对方的存在的。这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
2.广播
2.1 标准广播(Normal broadcasts)
标准广播是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播信息,因此它们之间没有任何先后顺序可言,receivers(接收器)的执行顺序不确定。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
2.2 有序广播(Ordered broadcasts)
有序广播是一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的可以先接收到消息,并且靠前的广播接收器可以截断正在传递的广播,这样后面的接收器会收不到这条广播消息 。优先级别在intent-filter中的priority中声明,-1000到1000之间,值越大优先级越高,同一优先级依然是随机接收顺序。
2.3 粘性广播(Sticky Broadcast)
粘性广播的特点是Intent会一直保留到广播事件结束,而这种广播也没有所谓的10秒限制,10秒限制是指普通的广播如果onReceive()方法执行时间太长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。该广播用sendStickyBroadcast发送。
3.动态注册
3.1 什么是动态注册
动态注册就是利用代码注册,动态注册的广播接收器可以自由的控制注册和取消,有很大的灵活性。但是只能在程序启动之后才能收到广播,广播接收器的注销是在onDestroy()方法中的。所以广播接收器的生命周期是和当前活动的生命周期一样。
3.2 动态注册步骤
(1)实例化自定义的广播接收器。
(2)创建IntentFilter实例。
(3)调用IntentFilter实例的addAction()方法添加监听的广播类型。
(4)最后调用Context的registerReceiver(BroadcastReceiver,IntentFilter)动态的注册广播
3.3 动态注册监听网络变化
public class MainActivity extends AppCompatActivity {
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();
}
}
}
在MainActivity中定义一个内部类NetworkChangeReceiver,这个类继承自BroadcastReceiver,并重写了父类的onReceive()方法。当网络状态发生变化时,onReceive()方法就会得到执行。
具体过程:在onCreate()方法中先创建一个intentFilter实例,并给它添加一个值为android.net.conn.CONNECTIVITY_CHANGE的action。当网络发生变化,系统发出的值为android.net.conn.CONNECTIVITY_CHANGE的广播正是广播接收器要监听的。接下来创建NetworkChangeReceiver
实例,调用registerReceiver()方法注册,将NetworkChangeReceiver和IntentFilter的实例传入,这样NetworkChangeReceiver就收到值为android.net.conn.CONNECTIVITY_CHANGE的广播,实现广播接听。最后动态广播接收器使用unregisterReceiver()方法取消注册。
注意:
1)发送广播之前,要先注册,不然根本没有接收者匹配,不注册接收者虽然不会出现任何错误或警告,但是只发送一个没有任何接收者的广播播毫无意义。
2)Android保护用户设备的安全与隐私,要求:如果程序需要进行一些对用户来说比较敏感的操作,就必须在配置文件中声明权限。即在AndroidManifest.xml文件中加入
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
4.静态注册
4.1 什么是静态注册
静态注册就是利用XML来注册。当我们需要一直接收某种广播时,可以使用静态注册方式。静态注册的程序,无论该程序是否启动,都会当广播到来时接收,并处理。而动态注册的程序只有在程序运行时才会收到广播消息,程序不运行了,它就收不到了。
可以使用Android Studio 中的快捷方法来创建广播接收器。
右击包名——New——Other——BroadcastReceiver
4.2 静态注册实现开机启动
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"Boot Complete",Toast.LENGTH_LONG).show();
}
}
在创建好的广播接收器中修改代码,在onReceive()方法中使用toast弹出一段提示信息。这时AndroidManifest.xml中的标签内出现了一个新的标签
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true"></receiver>
由于Android系统启动完成后会打出一条值为android.intent.action.BOOT_COMPLETED的广播,因此添加对应action,
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
监听系统开机广播也是要声明权限的,如下:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
5.发送标准广播
新建一个接收器MyBroadcastReceiver,当MyBroadcastReceiver收到自定义的广播时,就会弹出"received in MyBroadcastReceiver"的提示。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_LONG).show();
}
}
修改AndroidManifest.xml文件,指定MyBroadcastReceive接收广播
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
在布局文件中添加一个按钮,用于发送广播的触发点
<Button
android:id="@+id/button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login"
/>
修改MainActivity代码,在按钮的点击事件里加入了发送自定义广播的逻辑。先构建一个intent对象,并把要发送的广播的值传入,然后调用了Context的sendBroadcast()方法将广播发送出去,这样所有监听com.example.broadcasttest.MY_BROADCAST这条广播的广播接收器都会收到消息。这时发出的就是一条标准广播。
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
Intent intent =new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);
}
});
6.发送有序广播
在标准广播的基础上,修改MainAcivity中的代码,将 sendBroadcast(intent);改为sendOrderedBroadcast(intent,null);
前一个参数为intent,后一个参数为权限相关的字符串。
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
Intent intent =new Intent("com.example.broadcasttest.MY_BROADCAST");
sendOrderedBroadcast(intent,null);
}
});
在注册时进行设定广播接收器的先后顺序,修改AndroidManifest.xml,通过android:priority属性给广播接收器设置优先级。
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
若要选择是否允许广播继续传递,需要在onReceive()方法中调用abortBroadcast(),表示将广播截断,后面的广播接收器无法收到这条广播。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_LONG).show();
abortBroadcast();
}
}
7.发送本地广播
public class MainActivity extends AppCompatActivity {
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_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
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();
}
}
}
代码部分与动态广播接收器类似,区别在于通过LocalBroadcastManager的getInstance()方法得到一个实例,然后注册广播接收器时调用localBroadcastManager的registerReceiver()方法,发送时调用 localBroadcastManager的sendBroadcast()方法。
本地广播特点:
1)本地广播只会在程序内传播。可以避免数据泄漏。
2)无法使用静态注册方式接收。因为静态注册是为了让程序在未启动难过的情况下也能接收广播,而发送本地广播时,程序一定是已经启动了。
3)发送本地广播会比发送系统全局广播更高效。