广播机制详解

广播机制简介

Android提供了一套完整的API,允许应用程序自由地发送和接收广播。发送广播的方法既即Intent,而接收广播的方法则是用广播接收器。

Android中的广播主要可以分为两种类型:

  • 标准广播:这是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序科研。这种广播的效率会比较高,但却无法被截断。
  • 有序广播:这是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播。

接收系统广播

动态注册监听网络变化

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

动态注册的广播必须要在程序启动之后才能接收到广播。

动态注册只需新建一个类,让他继承自BraodcastReceiver并重写onReceive()方法即可。

新建一个BroadcastTest项目,修改代码为如下:

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

当网络状态发送变化时,系统发出一条值为android.net.conn.CONNECTIVITY_CHANGE的广播,所以这里向intentFilter传入android.net.conn.CONNECTIVITY_CHANGE这一值,即相应的action。

接下来registerReceiver()用来注册从而使networkChangeReceiver能够接收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播。

PS:bluestacks模拟器没有网络选项,没法测试;得上真机,或者可以的话看看Android Studio的模拟器可以不可以。

然后我们可以修改onReceive()为如下代码以显示有无网络:

1
2
3
4
5
6
7
8
9
public void onReceive(Context context, Intent intent){
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();
}
}

同时在AndroidManifest.xml中添加权限:

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

这样就可以在真机上测试了。

静态注册实现开启启动

新建一个广播接收器:点击com.example.broadcasttest包->New->Other->Broadcast Receiver,创建一个名为BootCompleteReceiver的接收器,

勾选Exported(是否允许这个广播接收器接收本程序以外的广播)和Enabled(是否启用这个广播)这两个选项,完成创建。并修改代码如下:

1
2
3
4
5
6
7
public class BootCompleteReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}
}

另外还需添加如下权限:

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

并修改receiver标签如下:

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

这样开机时就可以接收到开机广播了。

发送自定义广播

发送标准广播

首先新建一个MyBroadcastReceiver广播接收器来接收我们发送的广播:

1
2
3
4
5
6
7
public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "I have received", Toast.LENGTH_SHORT).show();
}
}

然后再<receiver>标签修改:

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>

接下来在布局中添加一个按钮:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Broadcast"
/>
</LinearLayout>

然后修改MainActivity的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button 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);
}
});
}
}

这样点下按钮后就可以发送接收到广播了。同时,因为广播是使用Intent传递的,所以还可以在Intent中携带一些数据传递给广播接收器。

发送有序广播

新建一个新的BroadcastTest2项目。

再新建一个名为AnotherBroadcastReceiver的接收器,修改为如下代码:

1
2
3
4
5
6
7
public class AnotherBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();
}
}

然后继续添加intent-filter标签:

1
2
3
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>

然后返回刚才按钮的界面发送广播,即可得证我们的应用程序发出的广播是可以被其他应用程序接收到的。

回到BroadcastTest项目中,修改如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button 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");
sendOrderBroadcast(intent, null);
}
});
}

sendOrderBroadcast()用于发送有序广播,第二个参数是一个与权限有关的字符串。不过光是这样还不够,因为没有截断,所以还是会像标准广播一样两边都接收到,所以要再做以下修改:

修改MyBroadcastReceiver的标签内容:

1
2
3
4
5
6
7
8
<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>

设置优先级为100。再修改MyBroadcastReceiver代码为如下:

1
2
3
4
5
6
7
8
public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "I have received", Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}

利用abortBroadcast()方法来截断这条广播。

这样就可以验证为有序广播了。

使用本地广播

前面接收和发送的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且也可以接收来自其他任何应用程序的广播。

本地广播发出的广播则只能在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。

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
36
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);
button.setOnClickListener(new View.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();
}
}
}

这样就可以完成发送com.example.broadcasttest.LOCAL_BROADCAST这条广播,并在localReceiver里去接收这条广播的功能。

注意,本地广播是无法通过静态注册的方式来接收的。

总结

Android的广播分为两类:标准广播和有序广播。标准广播无法被截断,而有序广播中低优先级的接收器可以被高优先级的接收器截断广播。注册广播的方式有两类,动态注册和静态注册,其中动态注册的广播接收器必须要在程序启动后才能接收,而静态注册则不受限制,可以做到接收手机开机的广播。最后还有本地广播,可以限制广播只在当前应用程序中传播,并且可以限制只接收当前应用程序的广播。