0%

Android系统广播机制

广播机制简介

Android中有灵活的广播机制,Android中的每个应用程序都可以对自己感兴趣的广播进行注册,这样应用就只会收到自己感兴趣的广播,广播可以是来自于系统的,也可以来自于其他应用程序。Android系统中分为标准广播和有序广播:

  • 1,标准广播:
    是一种完全异步执行的广播,广播发出后,所有的广播接收器几乎都会在同一时刻接收到这条广播信息,因此他们之间没有任何先后顺序。它的优点是:传播效率较高。缺点是:无法截断。
  • 2,有序广播:
    特点是同步执行,可以被截断,同一时刻只会有一个广播可以接收到这条广播消息,当这个广播接收器的逻辑执行完以后,广播才会继续传递,所以广播有先后顺序,优先级高的就先接收到广播信息,优先级低的就后接收信息,广播可以被截断,被优先级高的广播接收器截断后后面的广播接收器就不能接收到广播信息了。

接收系统广播

Android内置了许多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息,下面讲讲它的具体用法

动态注册监听网络变化

注册广播的方式有两种,在代码中注册和在AndroidManifest中注册。前者也称为动态注册,后者也称为静态注册。

新建一个类,让它继承自BroadCastReceiver,重写onReceive()方法就行了,这样当有广播到来时,onReceive()方法就会执行,具体的逻辑就在这里执行。

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

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();

}

}

}

定义了一个内部类NetworkChangeReceiver,继承自BroadcastReceiver并重写了onReceive()方法,这样每当网络状态变化时,onReceice方法会得到执行,当然这里只是简单用Toast提示了信息。

onCreate方法中,创建了IntentFilter实例,并给它添加了一个”android.net.conn.CONNECTIVITY_CHANGE”的action,添加这个值的目的是当网络状态发生变化时,系统发出的正是一条值为”android.net.conn.CONNECTIVITY_CHANGE”的广播,也就是说我们要接收什么广播我们就在这里添加相应的action。接下来创建一个NetworkChangeReceiver的实例,然后调用registerReceiver方法进行注册,”registerReceiver(networkChangeReceiver, intentFilter);”,这里我们把NetworkChangeReceiver的实例和IntentFilter的实例都传了进去,这样NetworkChangeReceiver就会收到”android.net.conn.CONNECTIVITY_CHANGE”这个广播。也就实现了监听网络变化的功能。

动态注册的广播一定要取消注册才行,我们在onDestroy方法里调用unregisterReceiver(networkChangeReceiver);方法来取消注册广播。

我们还可以优化onReceive方法中的代码,让程序准确地告诉我们当前是有网络还是没有网络,对代码进行进一步优化:

1
2
3
4
5
6
7
8
9
10
ConnectivityManager connectionManager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectionManager.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();
}

在onReceive方法中,通过getSystemService()方法得到了ConnectivityManager实例,这是一个系统服务类,专用于管理网络连接的,然后调用它的getActivityNetworkInfo()方法可以得到NetworkInfo的实例,然后调用NetworkInfo的isAvailable()方法判断当前是否有网络了,最后用toast提示。

注意:敏感的操作必须在配置文件中声明权限才可以使用,否则程序会崩溃,这里就需要访问系统的网络状态,所以要声明权限的

1
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
静态注册实现开机启动

如果要让程序未启动的情况下就能收到广播呢?这就需要使用静态注册的方式了。

我们可以用Android studio的快捷方式来创建一个广播接收器,new–>Pther–>Broadcast Receiver,会弹出新建广播接收器的窗口,将它命名为BootCompleteReceiver,Exported属性表示是否允许广播接收器接收本程序以外的广播,Enabled属性表示是否启用这个广播接收器,勾选两个属性,点击Finish完成创建。

在onReceive方法中修改代码如下:

1
Toast.makeText(context, "Boot Completed", Toast.LENGTH_SHORT).show();

代码在onReceive()方法中使用Toast弹出一段提示信息。

另外,静态注册的广播要在清单文件中注册才可以使用,使用Android studio快捷方式创建的广播接收器Android studio会自动完成在AndroidManifest文件的注册

1
2
3
4
5
6
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">

</receiver>

不过我们还要在AndroidManifest文件中声明权限,因为监听系统开机广播也是需要声明权限的,我们在AndroidManifest文件中加入了:

1
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

权限。以及在receiver中添加相应的action。

1
2
3
4
5
6
7
 <receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<action android:name="android.intent.action.BOOT_COMPLETED" />

</receiver>

这样就可以接收启动广播了。重启手机或模拟器,在启动完成之后就会收到开机广播。

发送自定义广播

发送标准广播

先定义一个广播接收器,新建一个MyBroadcastReceiver,代码如下:

1
2
3
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
}

当自定义广播接收器收到自定义的广播时,就会弹出”received in MyBroadcastReceiver”的提示,同样要在AndroidManifest文件中进行修改:

1
2
3
4
5
6
7
8
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>

然后在activity_main.xml文件中定义一个Button,用来触发发送广播的触发点。修改MainActivity的代码:

1
2
3
4
5
6
7
8
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent); // 发送广播
}
});

就这样将广播发送出去,调用Context的sendBroadcast将广播发送出去。

运行程序,点击Send Broadcast按钮,就会收到这条广播(以Toast方式提示)。

发送有序广播

广播是一种可以跨进程的通信放心方式,这一点从前面接收系统广播就可以看出来了。

新建项目BroadcastTest2项目,再在这个项目下新建一个广播接收器,用于接收上一个程序中的自定义广播,新建AnotherBroadcastReceiver。

1
2
Toast.makeText(context,"received in AnotherBrocastReceiver",
Toast.LENGTH_SHORT).show();

在onReceive方法中弹出一段文本信息,然后去AndroidManifest文件中对这个广播接收器进行修改,如下:

1
2
3
4
5
6
7
8
 <receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>

我们设置AnotherbroadcastReceiver接收的同样是”com.example.broadcasttest.MY_BROADCAST”这条广播,将BroadcastTest2安装到模拟器中,然后重新回到BroadcastTest程序主界面,点击send Broadcast按钮,就会分别弹出两次提示信息。

这都是标准广播,下面我们来尝试发送有序广播,在BroadcastTest项目,修改MainActivity代码:

1
2
3
4
5
6
7
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendOrderedBroadcast(intent,null);
}
});

发送有序广播只要一行代码:sendOrderedBroadcast(intent,null);将sendBroadcast()改为sendOrderedBroadcast()方法,sendOrderedBroadcast()方法接收两个参数,第一个仍然是Intent,第二个是一个与权限有关的字符串,这里传入null就行了,运行程序,两个程序仍然可以接收这个广播。

不过有序广播是可以截断的,怎么设置先后顺序呢?在配置文件中这只优先级:

1
2
3
4
5
6
7
8
 <receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority=100>
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>

通过android:priority属性给广播接收器设置了优先级,优先级高的广播接收器就可以先接收广播,这里将MyBroadcastReceiver的优先级设置成100,保证它会比AnotherBroadcastReceiver先收到广播。

设置了优先级,那么MyBroadcastReceiver就可以选择是否截断广播,修改onReceive中的代码:

1
2
3
4
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
abortBroadcast();
}

abortBroadcast()方法就表示将这条广播截断,后续的广播接收器就不能接收这条广播了,重新运行程序,点击send Broadcast按钮,只有MyBroadcastReceiver中的Toast信息能够弹出,说明这条广播被它截断了。