Android引入廣播機制的用意。單線程模型Message、Handler、Message Que.

註冊廣播有幾種方式,這些方式有何優缺點?請談談Android引入廣播機制的用意。 android

Android 的廣播機制數據庫


在 Android 裏面有各類各樣的廣播,好比電池的使用狀態,電話的接收和短信的接收都會產生一個廣播,應用程序開發者也能夠監聽這些廣播並作出程序邏輯的處理。下面我畫一張粗略的圖來幫助你們理解廣播的運行機制。編程

 

 

Android 中有各式各樣的廣播,各類廣播在Android 系統中運行,當系統/應用程序運行時便會向 Android 註冊各類廣播,Android 接收到廣播會便會判斷哪一種廣播須要哪一種事件,而後向不一樣須要事件的應用程序註冊事件,不一樣的廣播可能處理不一樣的事件也可能處理相同的廣播事件,這時就須要 Android 系統爲咱們作篩選。數組

 

案例分析:緩存


一個經典的電話黑名單,首先經過將黑名單號碼保存在數據庫裏面,當來電時,咱們接收到來電廣播並將黑名單號碼與數據庫中的某個數據作匹配,若是匹配的話則作出相應的處理,好比掛掉電話、好比靜音等等。。。安全


 


Demo 分析:網絡


下面經過一個小DEMO 來說解一下廣播在Android 中如何編寫,在Demo中咱們設置了一個按鈕爲按鈕設置點擊監聽經過點擊發送廣播,在後臺中接收到廣播並打印LOG信息。代碼以下:app


 


BroadCastActivity 頁面代碼編程語言


public class BroadCastActivity extends Activity {
    public static final String ACTION_INTENT_TEST = "com.terry.broadcast.test";ide

     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button btn = (Button) findViewById(R.id.Button01);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub 
                Intent intent = new Intent(ACTION_INTENT_TEST);
                sendBroadcast(intent);
            }
        });
    }
}

 

    
  
  
接收器代碼以下: 
 


public class myBroadCast extends BroadcastReceiver {

     
    public myBroadCast() {
        Log.v("BROADCAST_TAG", "myBroadCast");
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub 
        Log.v("BROADCAST_TAG", "onReceive");
    }

}

 

 


    
    Android 廣播的生命週期 
在上面的接收器中,繼承了BroadcastReceiver 並重寫了它的onReceive 並構造了一個函數,下面經過圖片來一步一步認識 Android 廣播的生命週期。當我點擊一下按鈕,它向Android 發送了一個廣播,以下圖: 
     
這時咱們再點擊一下按鈕,它仍是會再向 Android 系統發送廣播,此時日誌信息以下: 
    
下面本人畫一張圖像,描述了Android 中廣播的生命週期,其次它並不像Activity 同樣複雜,運行原理很簡單以下圖: 
     
下面來看一下SDK給出的解釋: 
    
大意爲:若是一個廣播處理完onReceive 那麼系統將認定此對象將再也不是一個活動的對象,也就會finished掉它。
至此,你們應該能明白 Android 的廣播生命週期的原理,代碼也不用多介紹,很簡單的一個發送廣播並處理廣播的Demo。 
  
    Android 如何判斷並篩選廣播? 
前 面說過 Android 的廣播有各式各樣,那麼Android 系統是如何幫咱們處理咱們須要哪一種廣播併爲咱們提供相應的廣播服務呢?這裏有一點須要你們注意,每實現一個廣播接收類必須在咱們應用程序中的 manifest 中顯式的註明哪個類須要廣播,併爲其設置過濾器,以下圖: 
     
Tip:action 表明一個要執行的動做,在Andriod 中有很action 好比 ACTION_VIEW,ACTION_EDIT

那麼有些人會問了,若是我在一個廣播接收器中要處理多個動做呢?那要如何去處理?

在Android 的接收器中onReceive 以經爲咱們想到的,一樣的你必須在Intent-filter 裏面註冊該動做,能夠是系統的廣播動做也能夠是本身須要的廣播,以後你之須要在onReceive 方法中,經過intent.getAction()判斷傳進來的動做便可作出不一樣的處理,不一樣的動做。具體你們能夠去嘗試測試一下。

 


小結:

   

在Android 中若是要發送一個廣播必須使用sendBroadCast 向系統發送對其感興趣的廣播接收器中。 
使用廣播必需要有一個intent 對象必設置其action動做對象 
使用廣播必須在配置文件中顯式的指明該廣播對象 
每次接收廣播都會從新生成一個接收廣播的對象 
在BroadCast 中儘可能不要處理太多邏輯問題,建議複雜的邏輯交給Activity 或者 Service 去處理 
  Android廣播機制(兩種註冊方法) 
在android下,要想接受廣播信息,那麼這個廣播接收器就得咱們本身來實現了,咱們能夠繼承BroadcastReceiver,就能夠有一個廣播接受器了。有個接受器還不夠,咱們還得重寫BroadcastReceiver裏面的onReceiver方法,當來廣播的時候咱們要幹什麼,這就要咱們本身來實現,不過咱們能夠搞一個信息防火牆。具體的代碼:


public class SmsBroadCastReceiver extends BroadcastReceiver    
{   
  
    @Override  
    public void onReceive(Context context, Intent intent)   
    {   
        Bundle bundle = intent.getExtras();   
        Object[] object = (Object[])bundle.get("pdus");   
        SmsMessage sms[]=new SmsMessage[object.length];   
        for(int i=0;i<object.length;i++)   
        {   
            sms[0] = SmsMessage.createFromPdu((byte[])object[i]);   
            Toast.makeText(context, "來自"+sms[i].getDisplayOriginatingAddress()+" 的消息是:"+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show();   
        }   
        //終止廣播,在這裏咱們能夠稍微處理,根據用戶輸入的號碼能夠實現短信防火牆。    
        abortBroadcast();   
    }   
       

 

  當實現了廣播接收器,還要設置廣播接收器接收廣播信息的類型,這裏是信息:android.provider.Telephony.SMS_RECEIVED

  咱們就能夠把廣播接收器註冊到系統裏面,可讓系統知道咱們有個廣播接收器。這裏有兩種,一種是代碼動態註冊:


//生成廣播處理    
smsBroadCastReceiver = new SmsBroadCastReceiver();   
//實例化過濾器並設置要過濾的廣播   


IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");

//註冊廣播    
BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter);  


一種是在AndroidManifest.xml中配置廣播


<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
      package="spl.broadCastReceiver"  
      android:versionCode="1"  
      android:versionName="1.0">  
    <application android:icon="@drawable/icon" android:label="@string/app_name">  
        <activity android:name=".BroadCastReceiverActivity"  
                  android:label="@string/app_name">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
           
        <!--廣播註冊-->  
        <receiver android:name=".SmsBroadCastReceiver">  
            <intent-filter android:priority="20">  
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>  
            </intent-filter>  
        </receiver>  
           
    </application>  
       
    <uses-sdk android:minSdkVersion="7" />  
       
    <!-- 權限申請 -->  
    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>  
       
</manifest>  

 

  兩種註冊類型的區別是:

     1)第一種不是常駐型廣播,也就是說廣播跟隨程序的生命週期。

     2)第二種是常駐型,也就是說當應用程序關閉後,若是有信息廣播來,程序也會被系統調用自動運行。

            
        
        
        
   

BroadcastReceiver用於監聽被廣播的事件

必須被註冊,有兩種方法:

一、在應用程序的代碼中註冊

註冊BroadcastReceiver:

registerReceiver(receiver,filter);

取消註冊BroadcastReceiver:

unregisterReceiver(receiver);

當BroadcastReceiver更新UI,一般會使用這樣的方法註冊。啓動Activity時候註冊BroadcastReceiver,Activity不可見時候,取消註冊。

二、在androidmanifest.xml當中註冊

<receiver>

    <intent-filter>

     <action android:name = "android.intent.action.PICK"/>

    </intent-filter>

</receiver>

使用這樣的方法註冊弊端:它會始終處於活動狀態,畢竟是手機開發,cpu和電源資源比較少,一直處於活動耗費大,不利。


            
        
        
        
    
    10. 請解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關係。
   

1. Android進程
    在瞭解Android線程以前得先了解一下Android的進程。當一個程序第一次啓動的時候,Android會啓動一個LINUX進程和一個主線程。默認的狀況下,全部該程序的組件都將在該進程和線程中運行。
同 時,Android會爲每一個應用程序分配一個單獨的LINUX用戶。Android會盡可能保留一個正在運行進程,只在內存資源出現不足時,Android 會嘗試中止一些進程從而釋放足夠的資源給其餘新的進程使用, 也能保證用戶正在訪問的當前進程有足夠的資源去及時地響應用戶的事件。Android會根據進程中運行的組件類別以及組件的狀態來判斷該進程的重要 性,Android會首先中止那些不重要的進程。按照重要性從高到低一共有五個級別:
前臺進程 
前臺進程是用戶當前正在使用的進程。只有一些前臺進程能夠在任什麼時候候都存在。他們是最後一個被結束的,當內存低到根本連他們都不能運行的時候。通常來講, 在這種狀況下,設備會進行內存調度,停止一些前臺進程來保持對用戶交互的響應。
可見進程 
可見進程不包含前臺的組件可是會在屏幕上顯示一個可見的進程是的重要程度很高,除非前臺進程須要獲取它的資源,否則不會被停止。
服務進程 
運 行着一個經過startService() 方法啓動的service,這個service不屬於上面提到的2種更高重要性的。service所在的進程雖然對用戶不是直接可見的,可是他們執行了用 戶很是關注的任務(好比播放mp3,從網絡下載數據)。只要前臺進程和可見進程有足夠的內存,系統不會回收他們。
後臺進程 
運 行着一個對用戶不可見的activity(調用過 onStop() 方法).這些進程對用戶體驗沒有直接的影響,能夠在服務進程、可見進程、前臺進 程須要內存的時候回收。一般,系統中會有不少不可見進程在運行,他們被保存在LRU (least recently used) 列表中,以便內存不足的時候被第一時間回收。若是一個activity正 確的執行了它的生命週期,關閉這個進程對於用戶體驗沒有太大的影響。
空進程 
未運行任何程序組件。運行這些進程的惟一緣由是做爲一個緩存,縮短下次程序須要從新使用的啓動時間。系統常常停止這些進程,這樣能夠調節程序緩存和系統緩存的平衡。
Android 對進程的重要性評級的時候,選取它最高的級別。另外,當被另外的一個進程依賴的時候,某個進程的級別可能會增高。一個爲其餘進程服務的進程永遠不會比被服 務的進程重要級低。由於服務進程比後臺activity進程重要級高,所以一個要進行耗時工做的activity最好啓動一個service來作這個工 做,而不是開啓一個子進程――特別是這個操做須要的時間比activity存在的時間還要長的時候。例如,在後臺播放音樂,向網上上傳攝像頭拍到的圖片, 使用service可使進程最少獲取到「服務進程」級別的重要級,而不用考慮activity目前是什麼狀態。broadcast receivers作費時的工做的時候,也應該啓用一個服務而不是開一個線程。
2. 單線程模型 
    當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。因此主線程一般又被叫作UI線程。在開發Android應用時必須遵照單線程模型的原則: Android UI操做並非線程安全的而且這些操做必須在UI線程中執行。
2.1 子線程更新UI 
Android的UI是單線程(Single-threaded)的。爲了不拖住GUI,一些較費時的對象應該交給獨立的線程去執行。若是幕後的線程來執行UI對象,Android就會發出錯誤訊息
CalledFromWrongThreadException。之後遇到這樣的異常拋出時就要知道怎麼回事了!
2.2 Message Queue 
     在單線程模型下,爲了解決相似的問題,Android設計了一個Message Queue(消息隊列), 線程間能夠經過該Message Queue並結合Handler和Looper組件進行信息交換。下面將對它們進行分別介紹:
1. Message 
    Message消息,理解爲線程間交流的信息,處理數據後臺線程須要更新UI,則發送Message內含一些數據給UI線程。
2. Handler 
    Handler處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理。後臺線程就是經過傳進來的 Handler對象引用來sendMessage(Message)。而使用Handler,須要implement 該類的 handleMessage(Message)
方法,它是處理這些Message的操做內容,例如Update UI。一般須要子類化Handler來實現handleMessage方法。
3. Message Queue 
    Message Queue消息隊列,用來存放經過Handler發佈的消息,按照先進先出執行。
    每一個message queue都會有一個對應的Handler。Handler會向message queue經過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾並按先進先出執行。但經過這兩種方法發送的消息執行的方式略有不一樣:經過sendMessage發送的是一個message對象,會被 Handler的handleMessage()函數處理;而經過post方法發送的是一個runnable對象,則會本身執行。
4. Looper 
    Looper是每條線程裏的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)創建Message Queue,但在子線程裏並無創建Message Queue。因此調用Looper.getMainLooper()獲得的主線程的Looper不爲NULL,但調用Looper.myLooper() 獲得當前線程的Looper就有可能爲NULL。
    對於子線程使用Looper,API Doc提供了正確的使用方法:


    這個Message機制的大概流程:
    1. 在Looper.loop()方法運行開始後,循環地按照接收順序取出Message Queue裏面的非NULL的Message。
    2. 一開始Message Queue裏面的Message都是NULL的。當Handler.sendMessage(Message)到Message Queue,該函數裏面設置了那個Message對象的target屬性是當前的Handler對象。隨後Looper取出了那個Message,則調用 該Message的target指向的Hander的dispatchMessage函數對Message進行處理。
    在dispatchMessage方法裏,如何處理Message則由用戶指定,三個判斷,優先級從高到低:
    1) Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數作處理工做;
    2) Handler裏面的mCallback指向的一個實現了Callback接口的對象,由其handleMessage進行處理;
    3) 處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,經過這個實現的handleMessage函數處理消息。
    因而可知,咱們實現的handleMessage方法是優先級最低的!
    3. Handler處理完該Message (update UI) 後,Looper則設置該Message爲NULL,以便回收!
    在網上有不少文章講述主線程和其餘子線程如何交互,傳送信息,最終誰來執行處理信息之類的,我的理解是最簡單的方法——判斷Handler對象裏面的Looper對象是屬於哪條線程的,則由該線程來執行! 
    1. 當Handler對象的構造函數的參數爲空,則爲當前所在線程的Looper; 
    2. Looper.getMainLooper()獲得的是主線程的Looper對象,Looper.myLooper()獲得的是當前線程的Looper對象。 
如今來看一個例子,模擬從網絡獲取數據,加載到ListView的過程:

這個例子,我本身寫完後以爲仍是有點亂,要稍微整理才能看明白線程間交互的過程以及數據的先後變化。隨後瞭解到AsyncTask類,相應修改後就很容易明白了!

 

    11. AIDL的全稱是什麼?如何工做?能處理哪些類型的數據
詳情請參看:http://buaadallas.blog.51cto.com/399160/372090
部分概念:
        在Android中, 每一個應用程序均可以有本身的進程. 在寫UI應用的時候, 常常要用到Service. 在不一樣的進程中, 怎樣傳遞對象呢?  顯然, Java中不容許跨進程內存共享. 所以傳遞對象, 只能把對象拆分紅操做系統能理解的簡單形式, 以達到跨界對象訪問的目的. 在J2EE中,採用RMI的方式, 能夠經過序列化傳遞對象. 在Android中, 則採用AIDL的方式. 理論上AIDL能夠傳遞Bundle,實際上作起來卻比較麻煩.    
   

AIDL(AndRoid接口描述語言)是一種藉口描述語言; 編譯器能夠經過aidl文件生成一段代碼,經過預先定義的接口達到兩個進程內部通訊進程的目的. 若是須要在一個Activity中, 訪問另外一個Service中的某個對象, 須要先將對象轉化成AIDL可識別的參數(多是多個參數), 而後使用AIDL來傳遞這些參數, 在消息的接收端, 使用這些參數組裝成本身須要的對象.

AIDL的IPC的機制和COM或CORBA相似, 是基於接口的,但它是輕量級的。它使用代理類在客戶端和實現層間傳遞值. 若是要使用AIDL, 須要完成2件事情: 1. 引入AIDL的相關類.; 2. 調用aidl產生的class.

AIDL的建立方法:

AIDL語法很簡單,能夠用來聲明一個帶一個或多個方法的接口,也能夠傳遞參數和返回值。 因爲遠程調用的須要, 這些參數和返回值並非任何類型.下面是些AIDL支持的數據類型:

1. 不須要import聲明的簡單Java編程語言類型(int,boolean等)

2. String, CharSequence不須要特殊聲明

3. List, Map和Parcelables類型, 這些類型內所包含的數據成員也只能是簡單數據類型, String等其餘比支持的類型.

相關文章
相關標籤/搜索