Android四大组件之广播接收器分析(笔记)

Android中广播只要分为两种:标准广播和有序广播

标准广播(Normal broadcasts):一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时意味着它无法被截断。

有序广播(Ordered broadcasts):一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级较高的广播接收器就可以先收到广播消息,并且前面的广播接收器可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。


动态注册监听网络变化

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

只需要新建一个类,让它继承自BroadcastReceiver并重写父类的onReceive()方法,这样当有广播到来是,onRreceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。
  1. public class MainActivity extends Activity {  
  2.   
  3.     private IntentFilter intentFilter;   
  4.     private NetworkChangeReceiver networkChangeReceiver;  
  5.   
  6.   
  7.     @Override  
  8.     protected void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.activity_main);  
  11.         intentFilter = new IntentFilter();  
  12.         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");  
  13.         networkChangeReceiver = new NetworkChangeReceiver();  
  14.         registerReceiver(networkChangeReceiver, intentFilter);  
  15.     }  
  16.   
  17.     @Override  
  18.     protected void onDestroy() {  
  19.         super.onDestroy();  
  20.         unregisterReceiver(networkChangeReceiver);  
  21.     }  
  22.     //内部类
  23.     class NetworkChangeReceiver extends BroadcastReceiver {  
  24.   
  25.         @Override  
  26.         public void onReceive(Context context, Intent intent) {  
  27.             Toast.makeText(context, "network changes",  
  28.                     Toast.LENGTH_SHORT).show();  
  29.         }  
  30.     } 

然后观察 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 中的代码,如下所示:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.         ......  
  3.   
  4.     class NetworkChangeReceiver extends BroadcastReceiver {  
  5.   
  6.         @Override  
  7.         public void onReceive(Context context, Intent intent) {  
  8.             ConnectivityManager connectivityManager = (ConnectivityManager)   
  9.                     getSystemService(Context.CONNECTIVITY_SERVICE);  
  10.               
  11.             NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();  
  12.             if (networkInfo != null && networkInfo.isAvailable()) {  
  13.                 Toast.makeText(context, "network is available",  
  14.                         Toast.LENGTH_SHORT).show();  
  15.             } else {  
  16.                 Toast.makeText(context, "network is unavailable",  
  17.                         Toast.LENGTH_SHORT).show();  
  18.             }  
  19.         }  
  20.   
  21.     }  
  22.   
  23. }  

        在 onReceive() 方法中,首先通过 getSystemService() 方法得到了 ConnectivityManager 的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的 getActiveNetworkInfo() 方法可以得到 NetworkInfo 的实例,接着调用 NetworkInfo 的 isAvailable() 方法,就可以判断出当前是否有网络了,最后我们还是通过 Toast 的方式对用户进行提示。

        另外,Android 系统为了保证应用程序的安全性做了规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以否则程序将会直接崩溃,比如这里查询系统的网络状态就是需要声明权限的。打开 AndroidManifest.xml 文件,在里面加入如下权限就可以查询系统网络状态了:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="com.example.broadcasttest"  
  3.     android:versionCode="1"  
  4.     android:versionName="1.0" >  
  5.   
  6.     <uses-sdk  
  7.         android:minSdkVersion="14"  
  8.         android:targetSdkVersion="17" />  
  9.   
  10.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  11.     ......  
  12.   
  13. </manifest>  


静态注册实现开机启动

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是卸载onCreate()方法中的。而静态注册可以让程序在未启动的情况下就能接收到广播。

新建一个BootCompleteReceiver继承自BroadcastReceiver
  1. public class BootCompleteReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();  
  6.     }    
  7. }
在这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml中将这个广播接收器的类名注册进去。
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="com.example.broadcasttest"  
  3.     android:versionCode="1"  
  4.     android:versionName="1.0" >  
  5.     ......  
  6.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  7.     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  
  8.   
  9.     <application  
  10.         android:allowBackup="true"  
  11.         android:icon="@drawable/ic_launcher"  
  12.         android:label="@string/app_name"  
  13.         android:theme="@style/AppTheme" >  
  14.         ......  
  15.         <receiver android:name=".BootCompleteReceiver" >  
  16.             <intent-filter>  
  17.                 <action android:name="android.intent.action.BOOT_COMPLETED" />  
  18.             </intent-filter>  
  19.         </receiver>  
  20.     </application>  
  21.   
  22. </manifest> 

终于,<application> 标签内出现了一个新的标签<receiver>,所有的静态注册的广播接收器都是在这里进行注册的。它的用法其实和<activity>标签非常相似,首先通过 android:name 来指定具体注册哪一个广播接收器,然后在 <intent-filter> 标签里加入想要接收的广播就行了,由于 Android 系统启动完成后会发出一条值为 android:intent.action.BOOT_COMPLETED 的广播,因此我们在这里添加了相应的 action。

        另外,监听系统开机广播也是需要声明权限的,可以看到,我们使用 <uses-permission> 标签又加入了一条 android.permission.RECEIVE_BOOT_COMPLETED 权限。


发送自定义广播

1.发送标准广播

在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发出去也是白发。
因此新建一个类继承自BroadcastReceiver
  1. public class MyBroadcastReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         Toast.makeText(context, "received in MyBroadcastReceive",  
  6.                 Toast.LENGTH_SHORT).show();  
  7.     } 

然后在AndroidManifest.xml中对这个广播接收器进行注册:
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="com.example.broadcasttest"  
  3.     android:versionCode="1"  
  4.     android:versionName="1.0" >  
  5.     ......  
  6.   
  7.    <application  
  8.         android:allowBackup="true"  
  9.         android:icon="@drawable/ic_launcher"  
  10.         android:label="@string/app_name"  
  11.         android:theme="@style/AppTheme" >  
  12.         ......  
  13.   
  14.       <receiver android:name=".MyBroadcastReceiver">  
  15.             <intent-filter>  
  16.                 <action android:name="com.example.broadcasttest.MY_BROADCAST"/>  
  17.             </intent-filter>  
  18.       </receiver>  
  19.     </application>  
  20.   
  21. </manifest> 

然后在活动中发送广播
  1. @Override  
  2. public void onClick(View v) {  
  3. Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");  
  4. sendBroadcast(intent);  

由于广播是使用Intent进行传递的,因此可以在Intent中携带一些数据传递给广播接收器。

发送有序广播

广播是一种可以跨进程的通信方式。(所有应用程序都能收到)

  1. @Override  
  2. public void onClick(View v) {  
  3. Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");  
  4. sendOrderedBroadcast(intent, null);  //第二个参数是一个与权限相关的字符串
发送有序广播紧需要使用sendOrderedBroadcast()方法。

设定广播接收器的先后顺序:
  1. <receiver android:name=".MyBroadcastReceiver">  
  2.   <intent-filter android:priority="100" >  //优先级设成100
  3.      <action android:name="com.example.broadcasttest.MY_BROADCAST"/>  
  4.   </intent-filter>  
  5. </receiver>  

截断有序广播

只要在接收的那个广播接收器的onReceive()方法使用abortBroadcast()方法就可以终止传递。
  1. public class MyBroadcastReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         Toast.makeText(context, "received in MyBroadcastReceive",  
  6.                 Toast.LENGTH_SHORT).show();  
  7.                 abortBroadcast();  
  8.        }  
  9. }  

使用本地广播

前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器发送各种垃圾广播。
为了能够简单地解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制发出的广播只能狗在应用程序内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。
本地广播的用法并不复杂,主要就是使用了一个LocalBroadcastManager来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
  1. public class MainActivity extends Activity {  
  2.   
  3.     private IntentFilter intentFilter;  
  4.   
  5.     private LocalReceiver localReceiver;  //
  6.   
  7.     private LocalBroadcastManager localBroadcastManager;  //
  8.   
  9.     @Override  
  10.     protected void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         setContentView(R.layout.activity_main);  
  13.         localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例  
  14.         Button button = (Button) findViewById(R.id.button);  
  15.         button.setOnClickListener(new OnClickListener() {  
  16.             @Override  
  17.             public void onClick(View v) {  
  18.                 Intent intent = new Intent(  
  19.                         "com.example.broadcasttest.LOCAL_BROADCAST");  
  20.                 localBroadcastManager.sendBroadcast(intent); // 发送本地广播  
  21.             }  
  22.         });  
  23.         intentFilter = new IntentFilter();  
  24.         intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");  
  25.         localReceiver = new LocalReceiver();  
  26.         localBroadcastManager.registerReceiver(localReceiver, intentFilter);  // 注册本地广播监听器  
  27.     }  
  28.   
  29.     @Override  
  30.     protected void onDestroy() {  
  31.         super.onDestroy();  
  32.         localBroadcastManager.unregisterReceiver(localReceiver);  
  33.     }  
  34.   
  35.     class LocalReceiver extends BroadcastReceiver {  
  36.   
  37.         @Override  
  38.         public void onReceive(Context context, Intent intent) {  
  39.             Toast.makeText(context, "received local broadcast",  
  40.                     Toast.LENGTH_SHORT).show();  
  41.         }  
  42.   
  43.     }  
  44.   
  45. }  

本地广播是无法通过静态注册的方式来接收的。因为静态注册主要是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此完全不需要使用静态注册的功能。

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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值