Android的四大組件之Broadcast Receiver

BroadcastReceiver(廣播接收器)是Android中的四大組件之一。
        下面是Android Doc中關於BroadcastReceiver的概述:
        ①廣播接收器是一個專注於接收廣播通知信息,並做出對應處理的組件。很多廣播是源自於系統代碼的──比如,通知時區改變、電池電量低、拍攝了一張照片或者用戶改變了語言選項。應用程序也可以進行廣播──比如說,通知其它應用程序一些數據下載完成並處於可用狀態。
        ②應用程序可以擁有任意數量的廣播接收器以對所有它感興趣的通知信息予以響應。所有的接收器均繼承自BroadcastReceiver基類。
        ③廣播接收器沒有用戶界面。然而,它們可以啓動一個activity來響應它們收到的信息,或者用NotificationManager來通知用戶。通知可以用很多種方式來吸引用戶的注意力──閃動背燈、震動、播放聲音等等。一般來說是在狀態欄上放一個持久的圖標,用戶可以打開它並獲取消息。
Android中的廣播事件有兩種,一種就是系統廣播事件,比如:ACTION_BOOT_COMPLETED(系統啓動完成後觸發),ACTION_TIME_CHANGED(系統時間改變時觸發),ACTION_BATTERY_LOW(電量低時觸發)等等。另外一種是我們自定義的廣播事件。
        廣播事件的流程
        ①註冊廣播事件:註冊方式有兩種,一種是靜態註冊,就是在AndroidManifest.xml文件中定義,註冊的廣播接收器必須要繼承BroadcastReceiver;另一種是動態註冊,是在程序中使用Context.registerReceiver註冊,註冊的廣播接收器相當於一個匿名類。兩種方式都需要IntentFIlter。
        ②發送廣播事件:通過Context.sendBroadcast來發送,由Intent來傳遞註冊時用到的Action。
        ③接收廣播事件:當發送的廣播被接收器監聽到後,會調用它的onReceive()方法,並將包含消息的Intent對象傳給它。onReceive中代碼的執行時間不要超過5s,否則Android會彈出超時dialog。

Broadcast Receiver接收系統自帶的廣播
我們做一個例子,功能是在系統啓動時播放一首音樂。
1、建立一個項目Lesson21_BroadcastReceiver,拷貝一首音樂進res/raw目錄
2、建立HelloBroadcastReceiver.java 內容如下:

Codepackage android.basic.lesson21;   
  
import android.content.BroadcastReceiver;   
import android.content.Context;   
import android.content.Intent;   
import android.media.MediaPlayer;   
import android.util.Log;   

public class HelloBroadReciever extends BroadcastReceiver {   
  
    //如果接收的事件發生   
    @Override  
    public void onReceive(Context context, Intent intent) {   
        //則輸出日誌   
        Log.e("HelloBroadReciever", "BOOT_COMPLETED!!!!!!!!!!!!!!!!!!!!!!!!!");   
        Log.e("HelloBroadReciever", ""+intent.getAction());   
  
       //則播放一首音樂   
        MediaPlayer.create(context, R.raw.babayetu).start();   
   }   
}  

 

3、在AndroidManifest.xml中註冊此Receiver :

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionname="1.0" android:versioncode="1" package="android.basic.lesson21">  
    <application android:icon="@drawable/icon" android:label="@string/app_name">  
        <activity android:label="@string/app_name" android:name=".MainBroadcastReceiver">  
            <intent -filter="">  
                <action android:name="android.intent.action.MAIN">  
                <category android:name="android.intent.category.LAUNCHER">  
            </category></action></intent>  
        </activity>  
    <!-- 定義Broadcast Receiver 指定監聽的Action -->  
    <receiver android:name="HelloBroadReciever">  
            <intent -filter="">  
                <action android:name="android.intent.action.BOOT_COMPLETED">  
            </action></intent>  
    </receiver>  
</application></manifest>  

4、發佈程序,啓動模擬器,可以在Logcat中看到:

同時能聽到音樂播放的聲音。說明我們確實接收到了系統啓動的廣播事件,並做出了響應。

 

自定義廣播
下面我們學習自己製作一個廣播。我們接着剛纔的例子,繼續寫下去。
5、在MainBroadcastReceiver.java中填寫如下代碼:

Codepackage android.basic.lesson21;   
  
import android.app.Activity;   
import android.content.Intent;   
import android.os.Bundle;   
import android.view.View;   
import android.widget.Button;   
  
public class MainBroadcastReceiver extends Activity {   
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   
        setContentView(R.layout.main);   
  
        Button b1 = (Button) findViewById(R.id.Button01);   
  
        b1.setOnClickListener(new View.OnClickListener() {   
  
            @Override  
            public void onClick(View v) {   
                //定義一個intent   
                Intent intent = new Intent().setAction(   
                        "android.basic.lesson21.Hello").putExtra("yaoyao",   
                        "yaoyao is 189 days old ,27 weeks -- 2010-08-10");   
                //廣播出去   
                sendBroadcast(intent);   
            }   
        });   
    }   
}  

6、更改 HelloBroadReceiver.java 內容如下:

Codepackage android.basic.lesson21;   
  
import android.content.BroadcastReceiver;   
import android.content.Context;   
import android.content.Intent;   
import android.media.MediaPlayer;   
import android.util.Log;   
  
public class HelloBroadReciever extends BroadcastReceiver {   
  
    //如果接收的事件發生   
    @Override  
    public void onReceive(Context context, Intent intent) {   
        //對比Action決定輸出什麼信息   
        if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){   
            Log.e("HelloBroadReciever", "BOOT_COMPLETED !!!!!!!!!!!!!!!!!!!!!!!!!");   
        }   
  
        if(intent.getAction().equals("android.basic.lesson21.Hello")){   
            Log.e("HelloBroadReciever", "Say Hello to Yaoyao !!!!!!!!!!!!!!!!!!!!!!!!!");   
            Log.e("HelloBroadReciever", intent.getStringExtra("yaoyao"));   
        }   
  
        //播放一首音樂   
        MediaPlayer.create(context, R.raw.babayetu).start();   
    }   
}  

7、更改 AndroidManifest.xml 內容如下:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.basic.lesson21" android:versionname="1.0" android:versioncode="1">  
    <application android:icon="@drawable/icon" android:label="@string/app_name">  
        <activity android:label="@string/app_name" android:name=".MainBroadcastReceiver">  
            <intent -filter="">  
                <action android:name="android.intent.action.MAIN">  
                <category android:name="android.intent.category.LAUNCHER">  
            </category></action></intent>  
        </activity>  
    <!-- 定義Broadcast Receiver 指定監聽的Action 這裏我們的接收器,接收了2個Action,一個系統的一個我們自定義的  -->  
    <receiver android:name="HelloBroadReciever">  
            <intent -filter="">  
                <action android:name="android.intent.action.BOOT_COMPLETED">  
            </action></intent>  
            <intent -filter="">  
                <action android:name="android.basic.lesson21.HelloYaoYao">  
            </action></intent>  
  
    </receiver>  
</application>  
<uses -sdk="" android:minsdkversion="8">  
</uses></manifest>

8、運行程序,點擊按鈕,查看LogCat,聽聽聲音


在使用Broadcast 時我們應該注意到,BroadcastReceiver的子類別都是無狀態的類別,每次收到發送廣播事件後,BroadcastReceiver都會創建一個新的對象,然後再執行onReceive()函數,當onReceive()函數執行完畢後,就立刻刪掉該對象,下一次再收到此廣播後,又會創建一個新的對象。所以說Broadcast組建是Android中最輕薄、最短小的組建。我們增加了一個static的變量numStatic ,和num變量 。代碼如下:

package com.androidtest.broadcaster;   
    
import android.content.BroadcastReceiver;   
import android.content.Context;   
import android.content.Intent;   
import android.util.Log;   
    
/**  
 * ClassName:Broadcaster  
 * Function: TODO ADD FUNCTION  
 * Reason:   TODO ADD REASON  
 *  
 * @author   Leon  
 * @version  
 * @since    Ver 1.1  
 * @Date     2011-6-9  
 */  
public class Broadcaster extends BroadcastReceiver{   
    
    private  static  final  String TAG = "Broadcaster";   
    private  static  int  numStatic  =100 ;   
    private  int  num =100 ;   
    @Override  
    public void onReceive(Context context, Intent intent) {   
    
        // TODO Auto-generated method stub   
        String string = intent.getAction();   
        numStatic= numStatic+50;   
        num=100+50;   
        Log.v(TAG  , "The action is "+ string + "Static Number is :" + numStatic   
                 + " Object num is :" + num);   
    
    }   
    
}  

多次發送廣播,然後輸出的結果如下,我們可以看到static Number 每次執行都會增加,而Object Num因爲每次都要創建所以一直都是一個固定的值。

上文中提到了BroadcastReceiver是Android中最輕薄、最短小的組件,它的對象生命週期十分短暫,經過傻蛋測試在BroadcastReceiver中讓線程睡眠10秒(Activity是5秒鐘)的話,Android就會彈出錯誤(和Activity超時的錯誤相同),同時需要注意的是Activity、Service和BroadcastReceiver都是運行在本進程的主線程裏面的。通過這個測試讓傻蛋進一步產生了疑問,如果在Service中處理一個長時間的任務會怎麼樣?
        啓動一個Service,然後在Service的onCreate()方法中添加如下代碼:

try {   
          Log.v(TAG , "sleep start …..");   
         Thread.sleep(20000);   
         Log.v(TAG,"sleep end …..");   
} catch (InterruptedException e) {   
     
         / / TODO Auto-generated catch block   
         e.printStackTrace();   
}    

很簡單就是讓Service睡眠20秒鐘,我們會發現,sleep start…. 和 sleep end….這兩個日誌打印出來了,但是後臺還會出現如下錯誤,前臺彈出no response超時對話框。

  在onCreate()中新啓動一個線程來,睡眠時,程序正常。
      所以總結一下:無論是 Activity、BroadcastReceiver還是Service,只要是有長時間處理的任務,就需要重新開一個線程來處理,爲什麼會這樣?因爲他們都是運行在主線程中的。
       在使用BroadcastReceiver時還有一個我們需要注意的:在BroadcastReceiver的onReceive(Context context , Intent intent )這第一個context到底是哪一個context?是Activity還是Application?通過測試發現:
如果你的BroadcastReceiver是通過在Activity中的this.registerReceiver(myBroadcaster, filter); 來註冊的話,那麼這個context就是這個Activity,而如果是通過AndroidManifest來註冊的話,那麼這個context就是:android.app.ReceiverRestrictedContext。


        BroadcastReceiver是接收從sendBroadcast()發出的intent的基類。你可以通過Context.registerReceiver()方法在代碼中動態的註冊一個BroadcastReceiver的實例,也可以通過再AndroidManifest.xml文件中用<receiver>標籤來靜態聲明。
       注意:如果你實在Activity.onResume()方法中註冊的一個receiver,那麼你必須在Activity.onPause()方法中進行註銷。(當一個activity處於暫停狀態是不會接收intents的,並且這樣做也可以減小系統不必要的開銷)。不要在Activity.onSaveInstanceState()方法中註銷receiver,因爲activity從棧中恢復的時候並不會調用這個方法了。
       可以接收的broadcast主要分爲兩種類型:
      普通的broadcasts(通過Context.sendBroadcast發送)是完全異步的。這個broadcast的receiver以無序的狀態運行,經常是在同一時刻運行。這種做法是十分高效的,但是也意味着receiver不能夠利用相互處理的結果或者是調用退出的API來退出(因爲不知道哪個receiver先接收到intent)。
      有序的broadcasts(通過Context.sendOrderedBroadcast發送)一次只發送給一個receiver。每一個receiver是有序的處理這個intent的,前面的receiver可以傳遞結果給下一個receiver,或者任意一個receiver都可以完全的退出,這樣intent就不會傳遞給其他的receivers.receiver的執行順序可以通過匹配的intent-filter中的android:priority屬性來控制;如果有多個receivers處於同一個優先級,那麼這幾個receivers將會以任意的順序來執行。
      即使是在廣播普通的broadcasts的情況下,系統也有可能在某些情況下轉換爲一次發送一個broadcast給一個receriver。特別是當receivers需要創建進程時,在同一時刻僅僅一個receiver可以運行,避免系統因爲這些新建的進程而過載。
      注意:儘管Intent類是用來發送和接受這些broadcasts,這裏的Intent broadcast機制和那些通過Context.startActivity()方法來啓動activity的intent是完全獨立的。一個BroadcastReceiver是沒辦法觀察和捕獲一個用於啓動activity的intent的;同樣的,當你通過intent來發出broadcast時,你也不可能(通過這個intent)找到或者啓動一個activity的。這兩種操作是完全不同的:通過一個intent來啓動一個activity是一個前臺操作,會改變用戶當前交互的對象;而通過intent來發出broadcast是一個後臺操作,用戶經常是察覺不到的。
      BroadcastReceiver類(通過一個manifest的<receiver>標籤作爲一個組件啓動)是應用程序全局聲明週期重要的一部分。

討論的主題
   1、Receiver的生命週期
   2、權限
   3、進程的生命週期
  
開發者指南
   更詳細的關於如何獲取和解析一個Intent的內容,請詳見Intents and Intent Filters開發者指南
  
Receiver的生命週期
       一個BroadcastReceiver的對象僅僅在調用onReceiver(COntext, Intent)的時間中有效。一旦你的代碼從這個函數中返回,那麼系統就認爲這個對象應該結束了,不能再被激活。
      你在onReceive(Context, Intent)中的實現有着非常重要的影響:任何對於異步操作的請求都是不允許的,因爲你可能需要從這個函數中返回去處理異步的操作,但是在那種情況下,BroadcastReceiver將不會再被激活,因此係統就會再異步操作之前殺死這個進程。
      特別是,你不應該再一個BroadcastReceiver中顯示一個對話框或者綁定一個服務。對於前者(顯示一個對話框),你應該用NotificationManagerAPI來替代,對於後者(綁定一個服務),你可以使用Context.startService()發送一個命令給那個服務來實現綁定效果。
權限
     存取的權限可以通過在發送方的Intent或者接收方的Intent中強制指定。
     在發送一個broadcast時強制指定權限,就必須提供一個非空的peemission參數給sendBroadcast(Intent, String)或者是sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handel, int, String, Bundle).只有那些擁有這些權限(通過在ANdroidManifest.xml文件中相應的聲明<uses-permission>標籤)的receiver能夠接收這些broadcast。
      在接收一個broadcast時強制指定權限,就必須在註冊receiver時提供一個非空的permission參數--無論是在調用registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)或者是通過再AndroidManifest.xml文件中通過<receiver>靜態標籤來聲明。只有那些擁有這些權限(通過在相應的AndroidManifest.xml文件中查詢<uses-permission>標籤來獲知)的發送方將能夠給這個receiver發送Intent。
      對於安全和權限的詳細內容請查看Security and Permission文檔。
進程的生命週期
      一個正在執行BroadcastReceiver(也就是,正在執行onReceive(COntext, Intent)方法)的進程被認爲是一個前臺的進程,將會一直運行,除非系統處於內存極度低的情況下。
      一旦從OnReceive()方法中返回,這個BroadcastReceiver將不會再被激活,此時它的主進程就和任何其他運行於此應用程序中的組件擁有相同的優先級。這一點非常重要,如果進程僅僅只是擁有BroadReceiver(一個普遍的情況是用戶從不或者是最近沒有和它進行交互),因此一旦它從onReceive()方法中返回時,系統就會認爲進程是空的並且主動的殺死它,以便這些資源可以被其他重要的進程利用。
      這意味着對於耗時的操作,可以採用將Service和BroadcastReceiver結合使用以確保執行這個操作的進程在整個執行過程中都保持激活狀態。

文章轉自: http://www.oschina.net/question/157182_45595



 

 

 

 

 


 

 

 


 

 

 

 

 



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