天天看點

Android 7.0 中 Service bind 流程詳解

我們知道,Android啟動Service有兩種方式,startService和bindService。

對于通過startService啟動的Service,隻能作為接收方,啟動方可以通過onStartCommand來傳遞參數。這種啟動方式其可以有多個啟動者,不過在銷毀的時候,一旦有任意一個啟動調用了stopService或者自身stopSelf後,該Service就會停止,而啟動者的生命周期無法影響到該Service。

對于通過bindService啟動的Service,其和啟動方有個“綁定”的過程,啟動方可以通過Service的binder引用來調用Service的方法。不過這種方式Service的生命周期會關聯着啟動方的,啟動方生命周期結束後,會預設unbindService來結束。

startService很多過程在bindService中都會得到展現,是以本文隻介紹bindService的流程。

先看看bindService的調用過程

bindService

bindServiceCommon

這一步中首先會把ServiceConnection轉成Binder對象

這一步是在LoadApk中完成的

可以看到,LoadApk會為每個context儲存一個key為ServiceConnection、value為ServiceDispatcher的map,這樣盡量做到了ServiceConnection對應的Binder對象的複用。如果沒有可複用,則把ActivityThread的主Handler和ServiceConnection對應起來,這樣在複用的時候,ServiceDispatcher會調用validate方法檢查handler為目前主線程handler,保證不錯亂。另外,ServiceConnection作為回調接口,其在IPC中實質是以ServiceDispatcher的内部類InnerConnection作為載體來代替的。

接下來,就交給ActivityManagerService處理了

ActiveService.bindServiceLocked

這步顧名思義,是擷取根據intent、binder等資料擷取Service的資訊,其中ServiceLookupResult包含了ServiceRecord和權限資訊。進入函數我們可以看到如下:

這一步可以看到,再取ServiceRecord的時候,會先去嘗試用緩存,如果設定了FLAG_EXTERNAL_SERVICE且是外部Service調用,則禁止複用。如果無法複用,則建立新的ServiceRecord并指派。

回到bindServiceLocked中,往下走

首先,ServiceRecord會根據自己的成員函數retrieveAppBindingLocked方法來擷取一個AppBindRecord。再此後面會用到一些資料結構:AppBindRecord、ServiceRecord、IntentBindRecord、ProcessRecord、ConnectionRecord。他們之間的關系需要理清楚一下:

ServiceRecord:儲存了Service的資訊

ProcessRecord:程序的資訊

ConnectionRecord:程序和Service建立起的本次通信,記一次ConnectionRecord

AppBindRecord:當某個程序需要用某個Intent啟動Service的時候,這時候應用程式和Service的關系由AppBindRecord來維持。是以裡面包含:誰啟動(ProcessRecord)、啟動的是哪個(ServiceRecord)、用什麼來啟動(IntentBindRecord)、所有啟動記錄的資訊(<code>ArraySet&lt;ConnectionRecord&gt;</code>)

IntentBindRecord:如在AppBindRecord所說,用什麼來啟動Service(Intent),裡面攜帶了ServiceRecord、及所有用此Intent啟動Service的<code>ArrayMap&lt;ProcessRecord, AppBindRecord&gt;</code>

舉個很不恰當的比喻:我想在一張紙上蓋一個“汪毅雄”的章。

ProcessRecord:我(行為發起者)

ServiceRecord:“汪毅雄”的章印(行為目标)

IntentBindRecord:制作刻了“汪毅雄”三個字、并且能記錄蓋章過程的一個智能章。(工具,有權限的人都能用。也可以有很多個不同的,隻要能蓋出“汪毅雄”這幾個字就可以)

AppBindRecord:“我要蓋章”這個過程一系列下來的所有記錄。

ConnectionRecord:手摁章印的過程。

有上面對資料結構的認識,我們可以看到,要取一個AppbindRecord。

a、我們先用Intent去擷取filter。

b、ServiceRecord裡有一個成員Map bindings儲存所有可以啟動該Service的IntentBindRecord,獲得filter後,利用它去bindings中嘗試複用IntentBindRecord。

c、拿到IntentBindRecord後,再用ProcessRecord去檢查IntentBindRecord,看看其裡面有沒有和ProcessRecord相同程序的且也是啟動該Service的AppbindRecord,有的話當然不用再建一個AppBindRecord,直接複用傳回。

繼續往下

獲得AppBindRecord後,再利用傳遞過來的資訊(包括IServiceConnection這個回調接口)建立一個ConnectionRecord,并且把ConnectionRecord分别儲存到AppBindRecord、ServiceRecord、ActivityRecord中。

如果flag不為BIND_AUTO_CREATE,或者bringUpServiceLocked成功(return null)後,則可以繼續後續的操作,如:Service已存在的時候BIND_TREAT_LIKE_ACTIVITY可以降低Service被殺的機率、重新回調connected、rebind等,本文不做讨論。繼續bringUpServiceLocked

在realStartServiceLocked之前,和Activity啟動一樣,我們先需要判斷Service的程序是否存在,如果不存在,需要孵化新的程序,然後再繼續Service的啟動。這塊在Activity啟動過程中已經詳述,這裡就不重複說了,詳見《Android 7.0中Launcher啟動Activity過程》

當程序存在的時候,我們直接realStartServiceLocked

上面可以看到,這塊利用binder通信去告訴程序去建立一個Service---scheduleCreateService,實際建立過程是在使用者程序(ActivityThread)中。

ActivityThread會建立一個CreateServiceData,并把ServiceInfo等資訊填入,交給main Handler處理,最終會到handleCreateService方法中。我們直接進入

在這個方法中,首先獲得Service的ClassLoader,用之load一個Service執行個體。然後再設定ContextImpl,然後初始化Service在程序中的資訊和其binder對象,最後調用Service.onCreate方法,完成建立。然後通過binder告訴AMS service建立成功,也就是serviceDoneExecuting,可以看到告訴AMS的有個type的字段設為SERVICE_DONE_EXECUTING_ANON。

這塊是Service生命周期給AMS的回調的類型,有3類。但是在Service onCreate完成後,AMS收到後沒太做處理,是以這部分跳過。

剛才在realStartServiceLocked,AMS告訴程序建立Service,但是步驟并沒有真正完成,我們回到剛才的方法

看到這裡,在建立完成後,會調用requestServiceBindingsLocked方法執行bind操作

AMS程序中,會對bindings中的每一個IntentBindRecord嘗試bind其程序。

通過binder告訴ActivityThread,Service需要執行bind操作。然後在ActivityThread會調用到handleBindService方法。

可以看到如果不是rebind,Service會執行Service.onBind()方法,把IntentBindRecord中的Intent告訴給Service誰bind它了。然後再告訴AMS publishService。

在publishServiceLocked中,對所有ConnectionRecord,把bind結果回調給其IServiceConnection。而IService的Stub在LoadApk中

之後會通過主handler post一個Connection Runnable

最終真正執行到ServiceConnection中的onServiceConnected方法,完成最終綁定的過程

整個Service的bind過程就完成了!