Android中广播只要分为两种:标准广播和有序广播
标准广播(Normal broadcasts):一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时意味着它无法被截断。
有序广播(Ordered broadcasts):一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级较高的广播接收器就可以先收到广播消息,并且前面的广播接收器可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。
动态注册监听网络变化
- 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 就行了。接下来创建了一个 NetworkChangeReceiver 的实例,然后调用 registerReceiver() 方法进行注册,将 NetworkChangeReceiver 的实例和 IntentFilter 的实例都传了进去,这样 NetworkChangeReceiver 就会收到所有值为 android.net.conn.CONNECTIVITY_CHANGE 的广播,也就实现了监听网络变化的功能。
动态注册的广播接收器一定都要取消注册才行,这里我们是在 onDestroy() 方法中通过调用 unregisterReceiver() 方法来实现的。
不过只是提醒网络发生了变化还不够人性化,最好是能准确地告诉用户当前是有网络还是没有网络,因此我们还需要对上面的代码进行进一步的优化。修改 MainActivity 中的代码,如下所示:
在 onReceive() 方法中,首先通过 getSystemService() 方法得到了 ConnectivityManager 的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的 getActiveNetworkInfo() 方法可以得到 NetworkInfo 的实例,接着调用 NetworkInfo 的 isAvailable() 方法,就可以判断出当前是否有网络了,最后我们还是通过 Toast 的方式对用户进行提示。
另外,Android 系统为了保证应用程序的安全性做了规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以,否则程序将会直接崩溃,比如这里查询系统的网络状态就是需要声明权限的。打开 AndroidManifest.xml 文件,在里面加入如下权限就可以查询系统网络状态了:
静态注册实现开机启动
- public class BootCompleteReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
- }
- }
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.broadcasttest"
- android:versionCode="1"
- android:versionName="1.0" >
- ......
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- ......
- <receiver android:name=".BootCompleteReceiver" >
- <intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
终于,<application> 标签内出现了一个新的标签<receiver>,所有的静态注册的广播接收器都是在这里进行注册的。它的用法其实和<activity>标签非常相似,首先通过 android:name 来指定具体注册哪一个广播接收器,然后在 <intent-filter> 标签里加入想要接收的广播就行了,由于 Android 系统启动完成后会发出一条值为 android:intent.action.BOOT_COMPLETED 的广播,因此我们在这里添加了相应的 action。
另外,监听系统开机广播也是需要声明权限的,可以看到,我们使用 <uses-permission> 标签又加入了一条 android.permission.RECEIVE_BOOT_COMPLETED 权限。
发送自定义广播
1.发送标准广播
- public class MyBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Toast.makeText(context, "received in MyBroadcastReceive",
- Toast.LENGTH_SHORT).show();
- }
- }
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.broadcasttest"
- android:versionCode="1"
- android:versionName="1.0" >
- ......
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- ......
- <receiver android:name=".MyBroadcastReceiver">
- <intent-filter>
- <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
- </intent-filter>
- </receiver>
- </application>
- </manifest>
- @Override
- public void onClick(View v) {
- Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
- sendBroadcast(intent);
- }
发送有序广播
- @Override
- public void onClick(View v) {
- Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
- sendOrderedBroadcast(intent, null); //第二个参数是一个与权限相关的字符串
- }
- <receiver android:name=".MyBroadcastReceiver">
- <intent-filter android:priority="100" > //优先级设成100
- <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
- </intent-filter>
- </receiver>
截断有序广播
- public class MyBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Toast.makeText(context, "received in MyBroadcastReceive",
- Toast.LENGTH_SHORT).show();
- abortBroadcast();
- }
- }
使用本地广播
- 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();
- }
- }
- }
- 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄露的问题。
- 其他的程序无法将广播发送到我们的程序的内部,因此不需要担心会有安全漏洞的隐患。
- 发送本地广播比起发送系统全局广播将会更加高效。