Android Intent原理分析

Android Intent原理分析
Revision History
Date
Issue
Description
Author
<18/09/2010>
<0.5>
wylhistory
目錄
1.    Abstract
2.    Introduction
3.    Intent的架構
4.    Intent的發送過程
4.1      Intent消息在發送進程的邏輯
4.2      Intent發送在服務器端的執行
4.2.1       進入消息隊列之前
4.2.2       進入消息隊列後的處理
4.2.3       消息的分發過程
4.2.4       deliverToRegisteredReceiver的邏輯
4.2.5       processCurBroadcastLocked的邏輯
4.2.6       startProcessLocked的邏輯
5.    Intent的接收過程
5.1      Receiver的註冊
5.2      scheduleReceiver
5.3      scheduleRegisteredReceiver的邏輯
6.    總結
7.    未分析
1.               Abstract
主要是分析一下android的IPC通訊之Intent;
2.               Introduction
任何一個操作系統,都有自己的IPC通訊機制,Android也不例外;
IPC通訊在linux下面通常包括共享內存,管道,消息隊列等,這其中共享內存的效率比較高,我想;
這裏將要說的Intent的通訊機制是基於Binder的,而Binder的機制本質上是共享內存;
Intent中文翻譯爲:n.意圖,意向,目的 a.專心的;急切的;沒有一個特別適合,所以我還是決定用英文;
它的作用,我想就是傳達一些信息給特定的對象,或者廣播一些信息給某些對象;這裏涉及兩方面的內容:
A) 消息的發送;
B) 消息的接收;
後面就會具體的展開;
討論之前先看一個簡單的例子:
Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(intent);
這是摘自HeadsetObserver.java的代碼;
後面將會以此爲例,分析發送和接收的過程;
3.               Intent的架構
Intent的架構包括三方面:
Client,也就是發送這個Intent的activity;
Server,也就是activityManagerService.java,它主要是負責分發這些Intent給適當的對象;
Target,也就是那些需要處理這個Intent的activity,我們稱爲Receiver;
需要大致的瞭解一下,Intent通常有哪些部分?我們常用的包括三方面:
A)    action,就是這個intent是想達到什麼目的,比如是想打電話,還是想告訴我們電池電量低?
B)      數據,也就是這個intent要處理的是這些數據,如果你是receiver的話,你需要考慮,你是否需要處理這個intent,這裏包括數據的URI,以及數據的類型;
C)      Category,這個就是需要處理這個Intent的activity的種類,這個種類是比較難以理解的,我想Google的本意是想區分一下不同的Activity的種類,比如對於CATEGORY_LAUNCHER,這個就表示它是一個啓動器,有些消息只需要由特定類型的activity來處理;
當然還有其它的一些屬性,但是我們經常遇到的就是這三個,而這三個裏面最常用的是Action;
這些項的作用,主要是被activityManagerService用來挑選適當的Activity來處理這個Intent;
好了,太多的概念,讓人有點頭暈,後面會再詳細的講;
4.               Intent的發送過程
4.1               Intent消息在發送進程的邏輯
回到我們先前的那個例子:
Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(intent);
第一句話是構造一個Intent,注意只傳入了一個參數,這個參數就是一個Action,沒有指定data以及Category;也就是說如果某個Receiver寫成這樣(在AndroidManifext.xml裏面):
<receiver android:name="MediaButtonIntentReceiver">
            <intent-filter>
              <action android:name="android.media.AUDIO_BECOMING_NOISY" />
            </intent-filter>
</receiver>
這個receiver的onReceive函數將會被調用,其中receiver表示處理這個Intent消息的類,而intent-filter表示這個receiver關心哪些Intent,這裏寫明瞭,我只關心,action == android.media.AUDIO_BECOMING_NOISY的Intent,如果是其它的Intent請別來煩我;
第二句話的目的就是把這個消息廣播出去,誰關心誰處理去,從此和我沒關係了;
我可以很負責任的說,這個mContext的類型爲ApplicationContext,在Android的代碼裏,這樣命名的變量多半是這個類型,所以後面的邏輯就簡單描述爲:
Android的代碼很多都這樣,一層層的調用,很多時候都是二傳手,這是模塊化設計需要付出的代價,不過,值得;
對於分析來說,需要理清楚這個調用到底去了什麼地方?
代碼在ActivityManagerNative.java裏面:
這是一個典型的Binder調用,從此以後代碼的執行進入了另外一個進程;
4.2               Intent發送在服務器端的執行
4.2.1          進入消息隊列之前
這個圖的邏輯是由ActivityManagerService.java來執行的,基本上也沒什麼意思;
重要的是最後調用的這個函數broadcastIntentLocked,基本上主要的工作都是由它來完成的;
這個函數非常重要,需要詳細分析:
1,首先是進行一些權限檢查,保證非串行的Intent其resultTo receiver必須是null;
2,如果這個Intent是說某個包被刪除了或者改變了,那麼當前的歷史棧裏面的屬於這個包的activity就必須被關掉;
3,如果是時區改變的消息,那麼將會先被放進隊列裏面通知當前正在運行的進程;
4,權限檢查,判斷是否有權限發送受保護的Intent,對於SYSTEM_UID,PHONE_UID,SHELL_UID,或者callingUid == 0的情況不做檢查,也就是說默認這些調用者有這個發送的權限;
5,對於sticky類型的Intent做一些特殊處理(關於sticky類型等概念後面會講),簡單就是把這個Intent加入到mStickyBroadcasts鏈表中去;
6,判斷這個Intent是有一個明確的對象,如果是那麼直接把它的對象加入到receivers列表中去,如果不是,那麼會繼續判斷是不是這個Intent在發送的時候設置了FLAG_RECEIVER_REGISTERED_ONLY標誌,如果是,那麼這個Intent將只發送給已經註冊的Receiver,不會發送給Broadcast receiver,否則就發送給所有的那些滿足條件的receivers;這裏就涉及Intent的匹配原則,主要是通過函數queryIntent(Intent intent, String resolvedType, boolean defaultOnly)來匹配的,原則就是我前面說的,根據Action,data type,category等來匹配,記住,每條規則之間的關係是或的關係,比如:
<receiver android:name="MediaButtonIntentReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
                <action android:name="android.media.AUDIO_BECOMING_NOISY" />
            </intent-filter>
        </receiver>         
就表示只要匹配到其中一條就算成功;另外,對於沒有寫明的匹配規則默認就算成功,比如對於此規則,沒有寫明數據類型,種類等,那麼默認所有的數據類型都可以匹配上;
7,如果是registeredReceivers不爲空並且這個Intent不是串行的,也就是上一步已經取出了對應的接收者,那麼就需要把這個Intent封裝成一個BroadcastRecord,然後加入到mParallelBroadcasts,這個稱爲並行廣播,也就是說可以同時發送給多個接收者,再通過scheduleBroadcastsLocked觸發真正的發送;
8,過濾一種特殊情況,也就是對於ACTION_PACKAGE_ADDED消息,這個被安裝的包本身不能作爲這個消息的接收者;
9,然後對registeredReceivers和receivers做一個合併,如果這兩個都不爲空的話,記住,合併前這個receivers標識了“具有固定對象的接收者或者是當前已經註冊的接收者不包括廣播接收者”,而registeredReceivers表示broadcast Filter,另外這步能合併的前提是這個Intent是串行的Intent,否則是不會合並的;
10,  合併以後receivers表示所有的串行receivers通過mOrderedBroadcasts.add(r)加入到列表中去,再通過scheduleBroadcastsLocked觸發真正的發送;
OK,這個函數基本上就結束了,這裏有三個概念需要解釋,串行,並行,sticky的BroadCast;
串行:就表示這個Intent必須一個一個的發送給接收者;
並行:表示這個Intent可以同時發送給多個接收者,通常廣播的消息都是並行的;
Sticky:這個類型的BroadCast比較難以理解,問了google也沒有答案,我個人的理解是這樣的,某些Intent需要被保留,當新的應用起來後,需要關注這個消息,但是呢,又不需要啓動這個應用來接收此消息,比如耳機插入等消息,這裏說實話,真的很巧妙,我們以前在maemo上碰到過這個問題,當時我們的策略是應用起來的時候自己查詢耳機的狀態,這裏的處理明顯就高明許多;
總結一下這個函數:它的主要作用是根據這個Intent的特點,構造BroadCastRecord加入到不同的列表,等待被處理;
OK,控制到了scheduleBroadcastsLocked這裏,它的邏輯很簡單:
private final void scheduleBroadcastsLocked() {
          if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendEmptyMessage(BROADCAST_INTENT_MSG);
        mBroadcastsScheduled = true;
}
先判斷mBroadcastsScheduled是否爲真,如果爲真就直接返回,這個變量主要是實現scheduleBroadcastsLocked和processNextBroadcast之間的順序執行,後面會看到在processNextBroadcast函數裏面會把它設置爲false;
下面就是通過BROADCAST_INTENT_MSG消息放入到消息隊列裏面,從這個角度來說Intent最後也是通過線程本身的消息隊列來實現Intent的分發的;
4.2.2          進入消息隊列後的處理
上面有提到會通過mHandler.sendEmptyMessage(BROADCAST_INTENT_MSG),把這個消息傳遞給mHandler,下面看看這個邏輯是如何實現的;
到這裏消息就是按照時間順序進入了mQueue了;
我們再看看一個activity的thread是如何進入主循環的:
             首先是通過prepareMainLooper建立基本的數據結構,包括mQueue以及mThread,mMainLooper;
          並把當前的這個Looper放入到線程獨有的變量中;
          其次是通過Looper.loop進入到主循環,邏輯如下:
                   首先是取出當前進入主循環的Looper,然後取出這個looper所擁有的mQueue,接下來就開始處理這個隊列裏面的消息了;
              根據處理方式分兩種消息,一種是消息的處理由一個線程來完成,一種是消息的處理時由一個函數來完成;
              後者的話也分兩種,一種是handler創建的時候提供了callback,這種情況非常少見;另外一種是通過handleMessage的方式來處理,通常我們在創建handler的時候都會提供這樣一個函數,於是消息就可以被處理了;
              注意,最左邊的分支我們還沒有討論,後面會遇到;
              我們先看看這個handleMessage對於BROADCAST_INTENT_MSG的處理:
                            這是最重要的函數,如果說broadcastIntentLocked是負責把Intent轉化爲BroadCast的話放入不同的隊列,那麼這個函數主要就是負責分發了,當然也涉及一點接收的流程;
4.2.3            消息的分發過程
下面分析函數private final void processNextBroadcast(boolean fromMsg);
1,先判斷fromMsg,如果是通過消息發送過來的就爲真,否則爲假,如果爲真mBroadcastsScheduled = false,這樣的話在函數scheduleBroadcastsLocked裏面就可以再次發送BROADCAST_INTENT_MSG的消息從而觸發processNextBroadcast函數被再次調用;
2,先判斷mParallelBroadcasts是否爲空,不爲空就開始調用這個列表裏面的receivers來接收消息,這個過程後面在串行intent的時候也會碰到,我們留到後面討論,這裏只需要知道它通過一個while循環把Intent發送給關注這個Intent的所有的receivers;
3,再判斷mPendingBroadcast是否爲空,如果不爲空,就表示先前發送的串行的Intent還沒有處理完畢,一般出現這種可能是因爲我們要發送到的receiver還沒有啓動,所以需要先啓動這個activity,然後等待起來的這個activity處理,這時候,這個mPendingBroadcast就爲true;如果發送這種情況需要判斷這個Activity是否死了,如果死了,那麼就把mPendingBroadcast設爲false,否則就直接返回,繼續等待;
4,接下來就順序的從mOrderedBroadcasts裏面取出BroadCastRecord消息,然後對這個消息的receiver一個一個的調用其接收流程,注意這裏要把這個BroadCast的所有的receivers串行發送,都發送完了,纔會進入到下一個BroadCastRecord消息;對於這個消息的處理,先判斷其接收者是不是BroadFilter,如果是,就調用deliverToRegisteredReceiver來接收,它的處理流程和前面的處理並行BroadCast一樣,所以留到後面講;
5,如果不是BroadCast Filter,就需要找出這個reiver所在的進程,這時候通常就是一個IntentFilter所在的進程,如果這個進程活着,那麼就調用processCurBroadcastLocked(r, app)來處理,否則
6,需要先啓動這個進程,這就是startProcessLocked做的事情,然後設置mPendingBroadcast = r,這樣等應用起來它會處理這個消息,後面會有進一步的說明;
到這裏這個函數就結束了,比較複雜,裏面還有一些安全的檢查等等,上面遺留了三個問題:
A)deliverToRegisteredReceiver的處理流程;
B)processCurBroadcastLocked的處理流程;
C)startProcessLocked以後的進程如何處理這個喚醒它的Intent;
4.2.4       deliverToRegisteredReceiver的邏輯
         這裏也分爲這個receiver是否啓動,如果已經啓動就通過binder調用到了接收 activity的進程裏面了,右邊的分支performReceive也會調用到activityThread這邊,留到接收過程再看;
4.2.5       processCurBroadcastLocked的邏輯
如下:
可以看到它和deliverToRegisteredReceive的最終差別,只在於一個調用的是ScheduleRegisterdReceiver,一個是scheduleReceiver,這兩個函數最後都會進入到目標activity的線程;
4.2.6       startProcessLocked的邏輯
從這裏可以看出最後通過Process.start啓動了ActivityThread.java的進程,我們看看這個線程啓動後的執行邏輯:
首先是在進入主循環之前調用attachApplication通過binder調用進入到activityManagerService.java的進程;
這個服務器進程在把我們先前設置的mPendingBroadcast設置爲null,表示這個pending的broadcat已經得到處理了,然後調用processCurBroadcastLocked來處理這個broadcast消息,最後通過app.thread.scheduleReceiver進入到目標線程的接收流程;
OK,到這裏的話所有的發送分發流程已經結束了,剩下的就是兩個接收函數還沒有討論一個就是ScheduleRegisterdReceiver,一個是scheduleReceiver;
5.               Intent的接收過程
5.1             Receiver的註冊
Receiver的註冊一般分爲動態註冊和靜態註冊,動態註冊就是通過API registerReceiver來註冊,靜態的一般就是寫在AndroidManifest.xml,比如我們在前面已經看到的:
  <receiver android:name="MediaButtonIntentReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
                <action android:name="android.media.AUDIO_BECOMING_NOISY" />
            </intent-filter>
  </receiver>
                至於它的原理以後在分析packageManger的時候再分析;
下面重點來看看這個動態註冊的邏輯:
   這兩條路徑都可能被走到,如果不在ApplicationContent環境裏面就需要通過context.registerReceiver來註冊了,經過幾層傳遞會通過registerReceiverInternal進入主題;
這個圖看起來複雜,其實很簡單,就是構造receiver放入到列表中去,只是中間又經歷了Binder,這些receiver也就是我們先前在發送的過程中看到的那些receiver,當然它們能進入到broadcast的列表還要看發送的intent是否滿足它們給自的filter;
   好,現在可以看看我們在發送階段遺留的兩個函數:
      scheduleReceiver
     scheduleRegisteredReceiver;
5.2             scheduleReceiver
它的入口通常是Binder的分發函數,如下:
   右下方的這個函數scheduleReceiver纔會真正調用到ActivityThread.java,這個就是目標activity的母體;
   前面兩個就是封裝參數,最後放入到消息隊列中,等待主循環的處理,這段邏輯我們前面已經看到了,就不再細說,總之會調用到handlemessage函數;
在收到這個消息的時候通過handleReceiver來處理;
這又是一個非常重要的函數,需要詳細分析:
1,取得這個Intent指向的component,包括包名,類名;
2,取得包信息,這個結構提供了getClassLoader接口;
3,通過java.lang.ClassLoader cl = packageInfo.getClassLoader取得classLoader;
4,動態創建一個receiver,receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
5,調用receiver.onReceive(context.getReceiverRestrictedContext(), data.intent),進入到真正的處理流程中去了;
6,調用finishReceiver來觸發ActivityManagerService這個消息到其它receivers的發送或者下一個broadcast的發送;
這其中最重要的就是這個onReceive函數,我們通常都會實現這麼一個函數,然後在裏面處理我們收到的消息;
5.3             scheduleRegisteredReceiver的邏輯
入口還是Binder得分發函數,如下:
這種處理在Android的代碼裏面隨處可見,都是在native文件裏面通過onTransact分發調用service文件裏面的同名函數來完成真正的功能;
邏輯如下:
   也就是說,這裏把參數打包放入到args裏面去,然後通過post放入到消息隊列裏面等待處理,後面的邏輯和一個消息的發送很相似,如下:
這裏需要關注兩個點,
一個就是m.callback=r,這個賦值會導致後面在分發消息的時候走不同的路徑;
Msg.target=this,表示將來分發的時候誰來處理這個消息,如果設置爲null將會導致主循環退出;
分發的邏輯前面我們有介紹就是dispatchMessage的時候,我們再看看這段代碼:
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
這裏就是需要先判斷msg.callback是否爲null,前面我們已經看到賦值了,所以這裏不爲null;
於是調用handleCallback,如下:
private final void handleCallback(Message message) {
        message.callback.run();
 }
這個callback我們也看到了其實就是我們封裝的Args的args,原型爲:
class Args implements Runnable,
也就是說它是一個類似線程的對象,它的run函數代碼有點多,所以畫了個圖:
基本上這個邏輯就和我們之前看到的邏輯一致了,會調用receiver提供的onReceive函數來處理,這個onReceive函數是需要我們自己提供的,裏面一般的邏輯都是根據不同的消息做不同的處理;
最後就是通過finishReceiver來觸發ActivityManagerService對Intent的其它receivers的發送;
6.               總結
需要總結一下,
Intent從使用的角度來說,就是構造Intent,提供適當的參數,比如Action,比如數據類型,數據的uri等,然後發送出去;接收方需要註冊一個receiver,然後提供onReceive函數就可以了;這個註冊可以簡單的寫在AndroidManifest.xml裏面也可以通過registerReceiver來完成;
發送的時候有三個API可以用:
sendBroadcast
sendStickyBroadcast
sendOrderedBroadcast
第一個用於發送並行廣播;
第二個用於發送粘性廣播;
第三個用於發送串行廣播;
從原理的角度來說,本質上都是通過共享內存把信息傳遞給ActivityManagerService,它查詢已經註冊的那些receiver的過濾器,看是否和這個Intent匹配,如果匹配成功就加入到這個Intent的receiver列表中去,當然要根據這個Intent的參數決定加入到並行,串行,還是sticky的列表中,再通過Message傳遞,到主循環的下一輪來分發;這時候控制已經到了另外一個進程,然後分發好以後再調用目標線程的處理函數,所以基本上就是涉及三個進程,源——>server——>receiver;當然,源和目的可以是同一個進程;
另外這裏需要處理一種情況,就是這個消息發送的時候,目標線程還沒有創建,比如我們系統裏面的校準程序,需要在第一次開機的時候執行,那麼就需要捕捉一個廣播消息,比如:
<receiver android:name="StartupIntentReceiver" >
            <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
                 <category android:name="android.intent.category.HOME" />
            </intent-filter>
   </receiver>
這個消息的意思就是說啓動已經完畢了;
處理這個消息的類是StartupIntentReceiver,首先包含這個receiver的主activity將會被執行,然後再執行這個接收類的onReceive來接收消息並處理,所謂主activity是這樣的:
<activity android:name=".CalibrationTest"
                  android:label="Calibration Test"
                  android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
                  android:configChanges="keyboard|keyboardHidden|navigation|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
就是activity filter裏面包含ACTION android.intent.action.MAIN,種類包含android.intent.category.LAUNCHER的activity;
這個啓動過程是由ActivityManagerService.java來完成的,我們不必關心;
OK,基本上就這些了,關於Activity本身的原理,需要專門的文檔來描述;
7.               未分析
1,包管理器的信息來源;
2,AndroidManifest.xml的解析;
3,權限的檢查;
4,其它;
作者:wylhistory

聯繫方式:[email protected]

轉載自 http://blog.chinaunix.net/uid-741742-id-359319.html

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