0%

Android 详解 BroadcastReceiver

1. Android 中 Broadcast 的类型

  • 广播性质的角度

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

    • 系统广播registerReceiver()/unregisterReceiver()
    • 自定义广播onReceive()/sendBroadcast()/sendOrderedBroadcast()/abortBroadcast()
    • 本地广播LocalBroadcastManager

2. 注册广播的方式有

  • 静态注册:在 AndroidManifest.xml 文件中注册
  • 动态注册:在代码中注册,同时动态注册的广播接收器一定都要取消注册才行。缺点是必须要在程序启动之后才能接收到广播(因为注册的逻辑一般是写在 onCreate() 方法中的)

3. Android 系统内置的系统级别的广播有

  • 手机开机广播、电池电量变化广播、时间或时区变化广播、网络连接情况变化广播等

4. 写一个通过动态注册的方式监听网络变化系统广播的 Demo

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
// 需要在 AndroidManifest.xml 文件中声明网络访问权限:<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
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) {
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();
}
}
}
}

5. 写一个通过静态注册的方式监听开机启动系统广播的 Demo

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
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}
}

// 在 AndroidManifest.xml 文件中进行注册
<manifest xmlns:android="http://schema.android.com/apk/res/android"
package="com.example.broadcasttest">

<use-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<use-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> // 声明监听系统开机权限

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

...
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true" // 是否启用这个广播接收器
android:exported="true"> // 是否允许这个广播接收器接收本程序以外的广播
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> //添加相应的 action
<intent-filter>
</receiver>
</application>

</manifest>

6. 处理广播接收时的注意事项

  • 不要在 onReceive() 方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当 onReceive() 方法运行了较长时间而没有结束时,程序就会报错(Activity 是 5 秒;Broadcast 是 10 秒;后台 Service 是 20 秒、前台 Service 是 200 秒
  • 广播接收器更多的是扮演打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务

7. 怎样发送一个自定义的标准广播

  • 新建一个 Receiver继承 BroadcastReceiver重写 onReceive() 方法
  • AndroidManifest.xml 文件中 <receiver> 标签内添加一个子标签 <intent-filter>,在子标签内增加一个自定义的触发广播接收器的 action
  • 在页面适当位置增加发送广播逻辑:sendBroadcast(new Intent("和 AndridManifest.xml 中一致的自定义的 action"));(还可以在 Intent 中携带一些数据传递给广播接收器)

8. 怎样发送一个自定义的有序广播

  • 新建一个 Receiver继承 BroadcastReceiver重写 onReceive() 方法
  • AndroidManifest.xml 文件中 <receiver> 标签内添加一个子标签 <intent-filter>,在子标签内增加一个自定义的触发广播接收器的 action
  • 可以在子标签 <intent-filter> 内增加一个 priority 属性设置优先级,优先级高的广播接收器就可以先收到广播
  • 在页面适当位置增加发送广播逻辑:sendOrderedBroadcast(new Intent("和 AndridManifest.xml 中一致的自定义的 action", null));(第二个参数是一个与权限相关的字符串,这里传入 null 就可以了)
  • 如需中断广播,在 onReceive() 方法的最后调用 abortBroadcast() 方法

9. 怎样理解本地广播

  • 系统全局广播可以被其他任何应用程序接收到,我们也可以接收到来自其他任何应用程序的广播。这样容易引起安全性问题和垃圾广播的问题
  • 本地广播只能在本地应用程序的内部进行传递,并且广播接收器也只能接受来自本应用程序发出的广播,这样就可以解决安全性问题
  • 本地广播是无法通过静态注册的方式来接收的,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播。而发送本地广播时,我们的程序肯定是已经启动了的,因此也完全不需要静态注册的功能

10. 本地广播的优点

  • 可以明确地知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露
  • 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患
  • 发送本地广播比发送系统全局广播更加高效

11. 写一个使用本地广播的 Demo

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
37
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(); //获取实例
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("自定义的 action 字符串");
localBroadcastManager.sentBroadcast(intent); //发送本地广播
}
});
intentFileter = new IntentFilter();
intentFilter.addAction("自定义的 action 字符串");
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();
}
}
}
-------------------- 本文结束感谢您的阅读 --------------------