【Android開發日記】之入門篇(六)——Android四大組件之Broadcast Receiver

廣播接受者是做爲系統的監聽者存在着的,它能夠監聽系統或系統中其餘應用發生的事件來作出響應。如設備開機時,應用要檢查數據的變化情況,此時就能夠經過廣播來把消息通知給用戶。又如網絡狀態改變時,電量變化時均可以經過廣播來通知用戶。要作比喻的話,廣播就像是咱們的感官,可以有效且快速的從外界獲取信息來反饋給自身。

1、廣播的功能和特徵

  1. 廣播的生命週期很短,通過 調用對象—實現onReceive—結束 整個過程就結束了。從實現的複雜度和代碼量來看,廣播無疑是最迷你的Android 組件,實現每每只需幾行代碼。廣播對象被構造出來後一般只執行BroadcastReceiver.onReceive方法,便結束了其生命週期。因此有的時候咱們能夠把它當作函數看也未必不可。
  2. 和全部組件同樣,廣播對象也是在應用進程的主線程中被構造,因此廣播對象的執行必須是要同步且快速的。也不推薦在裏面開子線程,由於每每線程還未結束,廣播對象就已經執行完畢被系統銷燬。若是須要完成一項比較耗時的工做 , 應該經過發送 Intent 給 Service, 由 Service 來完成。
  3. 每次廣播到來時 , 會從新建立 BroadcastReceiver 對象 , 而且調用 onReceive() 方法 , 執行完之後 , 該對象即被銷燬 . 當 onReceive() 方法在 10 秒內沒有執行完畢, Android 會認爲該程序無響應。

2、廣播事件監聽的兩種方法android

使用廣播進行事件監聽有兩種方法,靜態註冊和動態註冊,又或者稱冷插拔和熱插拔。靜態註冊就是將廣播接收器的相關信息寫在應用的配置文件中。當有廣播事件發生時,組件管理服務就會從安裝包管理服務中獲取已安裝應用的廣播組件信息。動態註冊則是經過Context.registerReceiver和Context.unregisterRecever,動態將廣播接收器與所須要監聽的事件綁定。web

  1. 靜態註冊
    首先是在應用的配置文件中寫入註冊消息,一樣是跟其餘組件同樣寫在application標籤以內
    <receiver android:name=".ColdReceiver"><!-- 你的Receiver名稱 -->  
       <intent-filter>  
          <action android:name="android.intent.action.COLD_BROADCAST"/>  <!-- 你廣播要接受的intent名稱 -->
          <category android:name="android.intent.category.DEFAULT" />  
       </intent-filter>  
    </receiver>

    而後咱們建一個ColdReceiver的類,繼承BroadcastReceiver,裏面代碼以下
    安全

    public class ColdReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            //跳轉到service中
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(1);
            //開啓service
            context.startService(intent);
            //日誌打印
            Log.d("TEST","靜態註冊");
        }
    
    }

    上面的Service啓動看到了嗎,這裏我用了上次說過的快捷跳轉。其中service的配置和內容以下
    網絡

    <service android:name=".BroadcastService"><!-- 你自定義的service文件   (在<application></application>裏面加)-->
            <intent-filter>
                    <action android:name="android.intent.action.BroadcastService" /><!-- 用intent啓動時的快捷名(也能夠用常規的方式啓動) -->
                    <category android:name="android.intent.category.default" />
            </intent-filter>
            </service>
    View Code
     1 public class BroadcastService extends Service{
     2 
     3     @Override
     4     public IBinder onBind(Intent intent) {
     5         // TODO Auto-generated method stub
     6         return null;
     7     }
     8     
     9     @Override
    10     public void onCreate() {
    11         //開啓服務時會首先調用該方法
    12         super.onCreate();
    13     }
    14 
    15     @Override
    16     public int onStartCommand(Intent intent, int flags, int startId) {
    17         //根據每次intent傳過來的信息進行判斷來顯示不一樣信息
    18         switch(intent.getFlags()){
    19             case 1:{
    20                 Toast.makeText(getApplicationContext(), "靜態註冊", Toast.LENGTH_SHORT).show();
    21                 break;
    22             }
    23             case 2:{
    24                 Toast.makeText(getApplicationContext(), "動態註冊", Toast.LENGTH_SHORT).show();
    25                 break;
    26             }
    27             case 3:{
    28                 Toast.makeText(getApplicationContext(), "普通廣播", Toast.LENGTH_SHORT).show();
    29                 break;
    30             }
    31             case 4:{
    32                 Toast.makeText(getApplicationContext(), "有序廣播", Toast.LENGTH_SHORT).show();
    33                 break;
    34             }
    35         }
    36         return START_STICKY;
    37     }
    38 
    39     @Override
    40     public void onDestroy() {
    41         // 中止service後會調用此方法
    42         Log.d("TEST", "destroy");
    43         super.onDestroy();
    44     }
    45 
    46 }
    View Code

    那麼靜態廣播的建立就完成了,簡單吧,就兩個步驟,一是配置廣播,二是繼承BroadcastReceiver,重寫裏面的onReceive函數。
    接下來咱們在新建一個工程來檢測廣播是否能夠響應消息。(檢測應用的代碼所有都在下方,而且不用再作更改了)
    app

     1 public class MainActivity extends Activity implements OnClickListener{
     2 
     3     private Button b1,b2,b3,b4;
     4     private Intent intent;
     5     @Override
     6     protected void onCreate(Bundle savedInstanceState) {
     7         super.onCreate(savedInstanceState);
     8         setContentView(R.layout.activity_main);
     9         intent = new Intent();
    10         //得到界面的控件
    11         b1 = (Button) findViewById(R.id.button1);
    12         b1.setOnClickListener(this);
    13         b2 = (Button) findViewById(R.id.button2);
    14         b2.setOnClickListener(this);
    15         b3 = (Button) findViewById(R.id.button3);
    16         b3.setOnClickListener(this);
    17         b4 = (Button) findViewById(R.id.button4);
    18         b4.setOnClickListener(this);
    19         Log.d("TEST","===初始化完成===");
    20     }
    21     
    22     @Override
    23     public void onClick(View v) {
    24         // TODO Auto-generated method stub
    25         switch(v.getId()){
    26             case R.id.button1:{//發送到靜態註冊廣播
    27                 intent = new Intent("android.intent.action.COLD_BROADCAST");  
    28                 sendBroadcast(intent);
    29                 //intent.putExtra("msg", "hello coldreceiver.");  
    30                 break;
    31             }
    32             case R.id.button2:{//發送到動態註冊廣播
    33                 intent = new Intent("android.intent.action.HOT_BROADCAST");  
    34                 //intent.putExtra("msg", "hello hotreceiver.");  
    35                 sendBroadcast(intent);
    36                 break;
    37             }
    38             case R.id.button3:{//普通廣播
    39                 intent = new Intent("android.intent.action.NORMAL_BROADCAST");  
    40                 sendBroadcast(intent);
    41                 break;
    42             }
    43             case R.id.button4:{//有序廣播
    44                 intent = new Intent("android.intent.action.SORT_BROADCAST");  
    45                 sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");  
    46                 break;
    47             }
    48         }
    49     }
    50     
    51     public void show(String str){
    52         Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    53     }
    54 
    55     @Override
    56     protected void onDestroy() {
    57         // TODO Auto-generated method stub
    58         super.onDestroy();
    59     }
    60 }
    View Code

    ok,將兩個應用都安裝到設備上,啓動測試用的應用,點擊第一個按鈕,運行的效果以下

    同時,會出現"靜態註冊"的Toast(不方便截圖)。能夠看出靜態註冊廣播可以跨應用來響應信息,這都要歸功於安卓上的組件管理服務,它會讀取每一個應用的配置文件,而後獲取裏面的組件信息,每當有消息響應時,組件管理服務會從中查找有沒有須要調用的組件,並判斷是否進行執行
    異步

  2. 動態註冊
    動態註冊也能夠分紅兩部分,一在代碼中進行動態註冊,二仍是繼承BroadcastReceiver,重寫裏面的onReceive函數。
    咱們在廣播應用中新建一個
    HotReceiver,繼承BroadcastReceiver
    public class HotReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            //String msg = intent.getStringExtra("msg");  
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(2);
            context.startService(intent);
            Log.d("TEST","動態註冊");
        }
    
    }

    在Activity中進行動態註冊
    ide

    public class MainActivity extends Activity {
    
        private HotReceiver receiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            //動態註冊廣播
            //新建一個廣播對象
            receiver = new HotReceiver();  
            //新建一個intent管理機制,(功能是對組件進行過濾,只獲取須要的消息)
            IntentFilter filter = new IntentFilter(); 
            //添加白名單(只獲取該動做的信息)
            filter.addAction("android.intent.action.HOT_BROADCAST");          
            //與廣播綁定,進行註冊
            registerReceiver(receiver, filter);
        }
    
        @Override
        protected void onDestroy() {
            //取消註冊,必定要記得,否則系統會報錯誤
            unregisterReceiver(receiver);
            stopService(new Intent("android.intent.action.BroadcastService"));
            super.onDestroy();
        }
    
    }

    ok,再使用測試應用來檢查一下效果,注意步驟,安裝好廣播應用打開,不要讓它退出,切換到測試用的廣播,點擊第二個按鈕。
    函數

    測試成功。那麼咱們關掉廣播應用在測試一下,會發現不會再出現動態註冊的打印消息。這說明動態註冊的廣播是與Activity綁定的,當Activity銷燬時,廣播也會被銷燬。
    在Android中,不少時候最好是使用動態註冊的方式使用廣播,好比時間變化事件,電量變動事件等,這些事件觸發率過高,若是使用靜態註冊,會致使進程頻繁的被構造和銷燬從而影響整個系統的效率。
    測試

3、廣播的兩種類型this

  1. 普通廣播
    普通廣播對於多個接收者來講是徹底異步的,一般每一個接收者都無需等待便可以接收到廣播,接收者相互之間不會有影響。對於這種廣播,接收者沒法終止廣播,即沒法阻止其餘接收者的接收動做。
    接下來咱們新建三個廣播來進行驗證。
    public class NormalReceiver1 extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(3);
            context.startService(intent);
            Log.d("TEST","普通廣播1");
            abortBroadcast();
        }
    
    }
    public class NormalReceiver2 extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(3);
            context.startService(intent);
            Log.d("TEST","普通廣播2");
            abortBroadcast();
        }
    
    }
    public class NormalReceiver3 extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(3);
            context.startService(intent);
            Log.d("TEST","普通廣播3");
            abortBroadcast();
        }
    
    }
    <receiver android:name=".NormalReceiver1">  
                <intent-filter>  
                    <action android:name="android.intent.action.NORMAL_BROADCAST"/>  
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </receiver> 
            <receiver android:name=".NormalReceiver2">  
                <intent-filter>  
                    <action android:name="android.intent.action.NORMAL_BROADCAST"/>  
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </receiver> 
            <receiver android:name=".NormalReceiver3">  
                <intent-filter>  
                    <action android:name="android.intent.action.NORMAL_BROADCAST"/>  
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </receiver>
    Xml Code

    安裝完畢後,咱們點擊檢測應用的第三個按鈕,能夠看到

    abortBroadcast()做用是阻斷廣播向下一級傳播,顯然在這裏不起做用,並會讓系統報錯。因此若是要讓廣播有必定的優先級進行傳播就要使用到有序廣播。
  2. 有序廣播
    有序廣播經過調用sendOrderedBroadcast函數進行發送。它每次只發送到優先級較高的接收者那裏,而後由優先級高的接受者再傳播到優先級低的接收者那裏,優先級高的接收者有能力終止這個廣播。在有序廣播的傳遞過程當中,每一個執行中的觸發器組件均可以經過BroadcastReceiver.setResult等函數附加額外的數據,而下一個廣播則可使用這些數據(BroadcastReceiver.getResultData)。這樣能夠構成一個消息數據處理鏈。
    爲了保證某一事件必定會被處理,能夠指明默認的廣播接收器(Final Receiver)。
    同樣的,新建三個廣播樣例
    public class SortReceiver1 extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
            //String msg = intent.getStringExtra("msg");  
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(4);
            context.startService(intent);
            Log.d("TEST","有序廣播1");
            abortBroadcast();
        }
    
    }
    public class SortReceiver2 extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
            //String msg = intent.getStringExtra("msg");  
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(4);
            context.startService(intent);
            Log.d("TEST","有序廣播2");
        }
    
    }
    public class SortReceiver3 extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
            //String msg = intent.getStringExtra("msg");  
            intent = new Intent("android.intent.action.BroadcastService"); 
            intent.addFlags(4);
            context.startService(intent);
            Log.d("TEST","有序廣播3");
        }
    
    }
    <receiver android:name=".SortReceiver1">  
                <intent-filter android:priority="1000">  
                    <action android:name="android.intent.action.SORT_BROADCAST"/>  
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </receiver> 
            <receiver android:name=".SortReceiver2">  
                <intent-filter android:priority="999">  
                    <action android:name="android.intent.action.SORT_BROADCAST"/>  
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </receiver> 
            <receiver android:name=".SortReceiver3">  
                <intent-filter android:priority="998">  
                    <action android:name="android.intent.action.SORT_BROADCAST"/>  
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </receiver>
    View Code

    咱們看到,如今這三個接收者的<intent-filter>多了一個android:priority屬性,而且依次減少。這個屬性的範圍在-1000到1000,數值越大,優先級越高。
    一樣發送廣播的代碼也是不同的

    sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");

    注意,使用sendOrderedBroadcast方法發送有序廣播時,須要一個權限參數,若是爲null則表示不要求接收者聲明指定的權限,若是不爲 null,則表示接收者若要接收此廣播,需聲明指定權限。這樣作是從安全角度考慮的,例如系統的短信就是有序廣播的形式,一個應用多是具備攔截垃圾短信 的功能,當短信到來時它能夠先接受到短信廣播,必要時終止廣播傳遞,這樣的軟件就必須聲明接收短信的權限。

    因此咱們在AndroidMainfest.xml中定義一個權限,並得到權限:(是要在廣播的應用中聲明)

    <permission android:protectionLevel="normal"  
                android:name="scott.permission.SORT_BROADCAST_PERMISSION" />
    <uses-permission android:name="scott.permission.SORT_BROADCAST_PERMISSION" />

    (這裏不是寫在application內部,而是同application同級)

    運行後只會出現這麼一個消息:

    由於在第一個廣播出咱們就終止了廣播的繼續傳遞,因此就只會出現這麼一條打印消息。

4、形形色色的廣播

在android中有不少系統自帶的intent.action,經過監聽這些事件咱們能夠完成不少功能。

  1. 開機:
    String BOOT_COMPLETED_ACTION 廣播:在系統啓動後。這個動做被廣播一次(只有一次)。監聽: 「android.intent.action.BOOT_COMPLETED」
  2. 電話撥入:
    String ANSWER_ACTION 動做:處理撥入的電話。監聽: 「android.intent.action.ANSWER」
  3. 電量變化:
    String BATTERY_CHANGED_ACTION 廣播:充電狀態,或者電池的電量發生變化。監聽: 「android.intent.action.BATTERY_CHANGED」
  4. 日期改變:
    String DATE_CHANGED_ACTION 廣播:日期被改變。 監聽:「android.intent.action.DATE_CHANGED」
  5. 取消更新下載:
    String FOTA_CANCEL_ACTION 廣播:取消全部被掛起的 (pending) 更新下載。 監聽:「android.server.checkin.FOTA_CANCEL」
  6. 更新開始安裝:
    String FOTA_READY_ACTION 廣播:更新已經被下載 能夠開始安裝。監聽 「android.server.checkin.FOTA_READY」
  7. 主屏幕:
    String HOME_CATEGORY 類別:主屏幕 (activity)。設備啓動後顯示的第一個 activity。 監聽:"android.intent.category.HOME」
  8. 新應用:
    String PACKAGE_ADDED_ACTION 廣播:設備上新安裝了一個應用程序包。監聽: 「android.intent.action.PACKAGE_ADDED」
  9. 刪除應用:
    String PACKAGE_REMOVED_ACTION 廣播:設備上刪除了一個應用程序包。監聽: 「android.intent.action.PACKAGE_REMOVED」
  10. 屏幕關閉:
    String SCREEN_OFF_ACTION 廣播:屏幕被關閉。監聽: 「android.intent.action.SCREEN_OFF」
  11. 屏幕開啓:
    String SCREEN_ON_ACTION 廣播:屏幕已經被打開。 監聽:「android.intent.action.SCREEN_ON」
  12. 時區改變:
    String TIMEZONE_CHANGED_ACTION 廣播:時區已經改變。監聽: 「android.intent.action.TIMEZONE_CHANGED」
  13. 時間改變:
    String TIME_CHANGED_ACTION 廣播:時間已經改變(從新設置)。 「android.intent.action.TIME_SET」
  14. 時間流逝:
    String TIME_TICK_ACTION 廣播:當前時間已經變化(正常的時間流逝)。 「android.intent.action.TIME_TICK」
  15. 進入大容量存儲模式:
    String UMS_CONNECTED_ACTION 廣播:設備進入 USB 大容量存儲模式。 「android.intent.action.UMS_CONNECTED」
  16. 退出大容量存儲模式:
    String UMS_DISCONNECTED_ACTION 廣播:設備從 USB 大容量存儲模式退出。 「android.intent.action.UMS_DISCONNECTED」
  17. 壁紙改變:
    String WALLPAPER_CHANGED_ACTION 廣播:系統的牆紙已經改變。 「android.intent.action.WALLPAPER_CHANGED」
  18. web搜索:
    String WEB_SEARCH_ACTION 動做:執行 web 搜索。 「android.intent.action.WEB_SEARCH」
  19. 網絡變化:
    String CONNECTIVITY_CHANGE_ACTION 動做:網絡變化。「android.intent.action.CONNECTIVITY_CHANGE_ACTION」
廣播自己的使用很簡單,它所包含的內容也不多,最可能是要多記住幾個系統經常使用的幾個ACTION。可是結合廣播來進行應用的開發卻會給用戶帶來更好的體驗,廣播的使用會讓用戶以爲開發者可以爲用戶考慮到各個狀況的發生,這點每每可以留住大多數的用戶。因此多考慮各類狀況的發生,不要吝嗇廣播的使用 ,爭取爲用戶留下一個好的印象吧。
 
資源下載:Demo
 
 ========================================
做者:cpacm
出處:(http://www.cpacm.net/2015/03/22/Android開發日記(四)——Android四大組件之Broadcast-Receiver/