Android中你需要知道的(一)

一、Activity的啓動過程

ActivityManagerService和ActivityStack位於同一個進程中,而ApplicationThread和ActivityThread位於另一個進程中。其中,ActivityManagerService是負責管理Activity的生命週期的,ActivityManagerService還藉助ActivityStack是來把所有的Activity按照後進先出的順序放在一個堆棧中;對於每一個應用程序來說,都有一個ActivityThread來表示應用程序的主進程,而每一個ActivityThread都包含有一個ApplicationThread實例,它是一個Binder對象,負責和其它進程進行通信。

        下面簡要介紹一下啓動的過程:
        Step 1. 無論是通過Launcher來啓動Activity,還是通過Activity內部調用startActivity接口來啓動新的Activity,都通過Binder進程間通信進入到ActivityManagerService進程中,並且調用ActivityManagerService.startActivity接口; 
        Step 2. ActivityManagerService調用ActivityStack.startActivityMayWait來做準備要啓動的Activity的相關信息;
        Step 3. ActivityStack通知ApplicationThread要進行Activity啓動調度了,這裏的ApplicationThread代表的是調用ActivityManagerService.startActivity接口的進程,對於通過點擊應用程序圖標的情景來說,這個進程就是Launcher了,而對於通過在Activity內部調用startActivity的情景來說,這個進程就是這個Activity所在的進程了;
        Step 4. ApplicationThread不執行真正的啓動操作,它通過調用ActivityManagerService.activityPaused接口進入到ActivityManagerService進程中,看看是否需要創建新的進程來啓動Activity;
        Step 5. 對於通過點擊應用程序圖標來啓動Activity的情景來說,ActivityManagerService在這一步中,會調用startProcessLocked來創建一個新的進程,而對於通過在Activity內部調用startActivity來啓動新的Activity來說,這一步是不需要執行的,因爲新的Activity就在原來的Activity所在的進程中進行啓動;
        Step 6. ActivityManagerServic調用ApplicationThread.scheduleLaunchActivity接口,通知相應的進程執行啓動Activity的操作;

        Step 7. ApplicationThread把這個啓動Activity的操作轉發給ActivityThread,ActivityThread通過ClassLoader導入相應的Activity類,然後把它啓動起來。


二、Android Binder深入理解

Android系統基本上可以看作是一個基於Binder通信的C/S架構。Binder就像網絡一樣,把系統各個部分連接在了一起。Android基於Client-Server的通信方式,諸如媒體播放,視音頻頻捕獲,到各種讓手機更智能的傳感器(加速度,方位,溫度,光亮度等)都由不同的Server負責管理,應用程序只需做爲Client與這些Server建立連接便可以使用這些服務.。

目前linux支持的IPC包括傳統的管道,System V IPC,即消息隊列/共享內存/信號量,以及socket中只有socket支持Client-Server的通信方式。

Android底層是基於Linux的,Android沒有選用傳統Linux的IPC方式,而利用Binder進行IPC通信,我覺得主要原因有以下兩點:

      一方面是傳輸性能。socket作爲一款通用接口,其傳輸效率低,開銷大,主要用在跨網絡的進程間通信和本機上進程間的低速通信。消息隊列和管道採用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開闢的緩存區中,然後再從內核緩存區拷貝到接收方緩存區,至少有兩次拷貝過程。共享內存雖然無需拷貝,但控制複雜,安全性較低,難以使用。 

      還有一點是出於安全性考慮。傳統IPC沒有任何安全措施,完全依賴上層協議來確保。Android爲每個安裝好的應用程序分配了自己的UID,傳統IPC的接收方無法獲得對方進程可靠的UID和PID(用戶ID進程ID),從而無法鑑別對方身份。Binder基於Client-Server通信模式,傳輸過程只需一次拷貝,爲發送發添加UID/PID身份,既支持實名Binder也支持匿名Binder,安全性高。

Binder 通信模型 

Binder框架定義了四個角色:Server,Client,ServiceManager(以後簡稱SMgr)以及Binder驅動。其中Server,Client,SMgr運行於用戶空間,驅動運行於內核空間。 

(1)Binder 驅動工作於內核態,驅動負責進程之間Binder通信的建立,Binder在進程之間的傳遞,Binder引用計數管理,數據包在進程之間的傳遞和交互等一系列底層支持。 
(2)ServiceManager 與實名Binder:SMgr的作用是將字符形式的Binder名字轉化成Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder實體的引用。

SMgr和其它進程同樣採用Binder通信,SMgr是Server端,有自己的Binder對象(實體),其它進程都是Client,需要通過這個Binder的引用來實現Binder的註冊,查詢和獲取。

在SystemServer中生成該Service並添加到ServiceManager中 

frameworks/base/services/java/com/android/server/SystemServer.java文件: 
 // add DevInfoService   
try {   
    Slog.i(TAG, "DevInfoManager Service");   
    devInfo =  new DevInfoService(context);   
    ServiceManager.addService(Context.DEVINFO_SERVICE, devInfo);   
} catch (Throwable e) {   
    reportWtf("starting devInfo Service", e);   
}   
// end 
在frameworks/base/core/java/android/app/ContextImpl.java中增加service註冊,如下: 

registerService(DEVINFO_SERVICE, new ServiceFetcher() {   
        public Object createService(ContextImpl ctx) {   
                IBinder b = ServiceManager.getService(DEVINFO_SERVICE);   
                IDevInfoManager service = IDevInfoManager.Stub.asInterface(b);   
                return new DevInfoManager(service, ctx.mMainThread.getHandler());   
        }   
}); 
(3)Client 獲得實名Binder的引用:從面向對象的角度,這個Binder對象現在有了兩個引用:一個位於SMgr中,一個位於發起請求的Client中。如果接下來有更多的Client請求該Binder,系統中就會有更多的引用指向該Binder,就象java裏一個對象存在多個引用一樣。而且類似的這些指向Binder的引用是強類型,從而確保只要有引用Binder實體就不會被釋放掉。通過以上過程可以看出,SMgr象個火車票代售點,收集了所有火車的車票,可以通過它購買到乘坐各趟火車的票-得到某個Binder的引用。 

(4)匿名 Binder:並不是所有Binder都需要註冊給SMgr廣而告之的。Server端可以通過已經建立的Binder連接將創建的Binder實體傳給Client,當然這條已經建立的Binder連接必須是通過實名Binder實現。由於這個Binder沒有向SMgr註冊名字,所以是個匿名Binder。Client將會收到這個匿名Binder的引用,通過這個引用向位於Server中的實體發送請求。匿名Binder爲通信雙方建立一條私密通道,只要Server沒有把匿名Binder發給別的進程,別的進程就無法通過窮舉或猜測等任何方式獲得該Binder的引用,向該Binder發送請求。(比如用戶自己寫的基於service的Binder就是匿名的) 

總結

Binder採用面向對象的設計思想,一個Binder實體可以發送給其它進程從而建立許多跨進程的引用;另外這些引用也可以在進程之間傳遞,就象java裏將一個引用賦給另一個引用一樣。爲Binder在不同進程中建立引用必須有驅動的參與,由驅動在內核創建並註冊相關的數據結構後接收方纔能使用該引用。

下面我們深入理解一下Binder 內存映射和接收緩存區管理 

暫且撇開Binder,考慮一下傳統的IPC方式中,數據是怎樣從發送端到達接收端的呢?通常的做法是,發送方將準備好的數據存放在緩存區中,調用API通過系統調用進入內核中。內核服務程序在內核空間分配內存,將數據從發送方緩存區複製到內核緩存區中。接收方讀數據時也要提供一塊緩存區,內核將數據從內核緩存區拷貝到接收方提供的緩存區中並喚醒接收線程,完成一次數據發送。這種存儲-轉發機制有兩個缺陷:首先是效率低下,需要做兩次拷貝:用戶空間->內核空間->用戶空間。Linux使用copy_from_user()和copy_to_user()實現這兩個跨空間拷貝。其次是接收數據的緩存要由接收方提供,可接收方不知道到底要多大的緩存纔夠用,只能開闢儘量大的空間或先調用API接收消息頭獲得消息體大小,再開闢適當的空間接收消息體。兩種做法都有不足,不是浪費空間就是浪費時間。 

Binder採用一種全新策略:由Binder驅動負責管理數據接收緩存。我們注意到Binder驅動實現了mmap()系統調用。 

Binder內核數據跨進程共享就是利用的Linux裏面的映射函數mmap()實現了內存映射,將一片內存區域映射到進程地址空間(覺得映射就像指針一樣,映射後進程就有指向這塊內存的指針,就能直接找到該內存存儲的數據進行訪問,進程就可以把這快內存當作自己內存區域堆中的一部分),此時該進程就可以向操作自己的內存一樣去操作映射過來的內存,而Binder也將該快內存映射到了Linux的內核空間,內核空間也就可以操作該內存快,當發送數據的進程發送數據時,Linux首先需要將數據拷貝到內核空間,此時內核時可以操作這個映射過來的內存的,所以就直接再用copy_from_user()拷貝該內存快,此時接受數據的進程也可以直接訪問這個數據,所以就免去來Linux內核再去執行copy_to_user()的操作,也就減少了一次數據拷貝。 


三、Android BroadcastReceiver深入理解

從實現原理看上,Android中的廣播使用了觀察者模式,基於消息的發佈/訂閱事件模型。因此,從實現的角度來看,Android中的廣播將廣播的發送者和接受者極大程度上解耦,使得系統能夠方便集成,更易擴展。具體實現流程要點粗略概括如下: 

1.廣播接收者BroadcastReceiver通過Binder機制向AMS(Activity Manager Service)進行註冊; 
2.廣播發送者通過binder機制向AMS發送廣播; 
3.AMS查找符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(一般情況下是Activity)相應的消息循環隊列中; 
4.消息循環執行拿到此廣播,回調BroadcastReceiver中的onReceive()方法。 
對於不同的廣播類型,以及不同的BroadcastReceiver註冊方式,具體實現上會有不同。但總體流程大致如上。 

簡單地說,Android廣播機制的主要工作是爲了實現一處發生事情,多處得到通知的效果。這種通知工作常常要牽涉跨進程通訊,所以需要由AMS(Activity Manager Service)集中管理。 

在Android系統中,接收廣播的組件叫作receiver,而且receiver還分爲動態和靜態的。動態receiver是在運行期通過調用registerReceiver()註冊的,而靜態receiver則是在AndroidManifest.xml中聲明的。動態receiver比較簡單,靜態的就麻煩一些了,因爲在廣播遞送之時,靜態receiver所從屬的進程可能還沒有啓動呢,這就需要先啓動新的進程,費時費力。另一方面,有些時候用戶希望廣播能夠按照一定順序遞送,爲此,Android又搞出了ordered broadcast的概念。 

靜態receiver 
靜態receiver是指那些在AndroidManifest.xml文件中聲明的receiver,它們的信息會在系統啓動時,由Package Manager Service(PKMS)解析並記錄下來。 
在系統啓動時,會啓動一個PackageManagerService服務,PackageManagerService服務會通過packagemanager對各個app安裝目錄的apk文件進行掃描解析,主要解析manifest裏面註冊的廣播接收器,並且最終將所有的接收器保存到了List<ResolveInfo>列表裏面。以後,當AMS調用PKMS的接口來查詢“和intent匹配的組件”時,PKMS內部就會去查詢當初記錄下來的數據,並把結果返回AMS。有的同學認爲靜態receiver是常駐內存的,這種說法並不準確。因爲常駐內存的只是靜態receiver的描述性信息,並不是receiver實體本身。 
當發出一個廣播ACTION_PRE_BOOT_COMPLETED時,PKMS向外界提供了queryIntentReceivers()函數,該函數可以返回一個List<ResolveInfo>列表,意思是查找有多少receiver對ACTION_PRE_BOOT_COMPLETED感興趣。

動態receiver 
 動態receiver必須在運行期動態註冊,其實際的註冊動作由ContextImpl對象完成。最終通過跨進程的方式將註冊信息發送到PackageManagerService中對其進行保存。 

發送廣播 
在註冊完了之後,廣播主要就是發送和處理了。這個過程都是在activitymanagerservice裏面進行的。 
ContextImpl.java文件中,可以看到很多的發送廣播函數,其中sendBroadcast()是最簡單的發送廣播的動作。 
我們熟悉的發送廣播代碼如下: 

mContext = getApplicationContext(); 
Intent intent = new Intent();   
intent.setAction("com.android.xxxxx");   
mContext.sendBroadcast(intent); 
上面的mContext的內部其實是在調用一個ContextImpl對象的同名函數,所以我們繼續查看ContextImpl.java文件。 

【frameworks/base/core/java/android/app/ContextImpl.java】 

@Override 
public void sendBroadcast(Intent intent) 
{ 
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());     
    try 
    { 
        intent.setAllowFds(false); 
        ActivityManagerNative.getDefault().broadcastIntent( 
            mMainThread.getApplicationThread(), intent, resolvedType, null, 
            Activity.RESULT_OK, null, null, null, false, false, 
            Binder.getOrigCallingUser()); 
    } catch (RemoteException e) { 
    } 
} 
ActivityManagerNative是一個Binder對象我們調用broadcastIntent屬於內部代理ActivityManagerProxy的方法,他將調用transact()方法將廣播的信息發送到ActivityManagerService。 

 public int broadcastIntent(IApplicationThread caller, 
         Intent intent, String resolvedType,  IIntentReceiver resultTo, 
         int resultCode, String resultData, Bundle map, 
         String requiredPermission, int appOp, boolean serialized, 
         boolean sticky, int userId) throws RemoteException 
 { 
     Parcel data = Parcel.obtain(); 
     Parcel reply = Parcel.obtain(); 
     data.writeInterfaceToken(IActivityManager.descriptor); 
     data.writeStrongBinder(caller != null ? caller.asBinder() : null); 
     intent.writeToParcel(data, 0); 
     data.writeString(resolvedType); 
     data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null); 
     data.writeInt(resultCode); 
     data.writeString(resultData); 
     data.writeBundle(map); 
     data.writeString(requiredPermission); 
     data.writeInt(appOp); 
     data.writeInt(serialized ? 1 : 0); 
     data.writeInt(sticky ? 1 : 0); 
     data.writeInt(userId); 
     mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0); 
     reply.readException(); 
     int res = reply.readInt(); 
     reply.recycle(); 
     data.recycle(); 
     return res; 
 } 
用戶進程把發送廣播的語義傳遞到AMS之後,最終會由AMS的broadcastIntentLocked(......)處理廣播的發送。broadcastIntentLocked()代碼太長,而且裏面有很多判斷,這裏就不貼出源碼了,下面我們可以將其邏輯大致整理成以下幾步: 
1) 爲intent添加FLAG_EXCLUDE_STOPPED_PACKAGES標記; 
2) 處理和package相關的廣播; 
3) 處理其他一些系統廣播; 
4) 判斷當前是否有權力發出廣播; 
5) 如果要發出sticky廣播,那麼要更新一下系統中的sticky廣播列表; 
6) 查詢和intent匹配的靜態receivers; 
7) 查詢和intent匹配的動態receivers; 
8) 嘗試向並行receivers遞送廣播; 
9) 整合(剩下的)並行receivers,以及靜態receivers,形成一個串行receivers表; 
10) 嘗試逐個向串行receivers遞送廣播。 

由於篇幅,先寫這麼多,希望對大家有幫助,接下來我會出“Android中你需要知道的(二)”,敬請期待!!!




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