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.   發送本地廣播比起發送系統全局廣播將會更加高效。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章