我們知道,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<ConnectionRecord></code>)
IntentBindRecord:如在AppBindRecord所說,用什麼來啟動Service(Intent),裡面攜帶了ServiceRecord、及所有用此Intent啟動Service的<code>ArrayMap<ProcessRecord, AppBindRecord></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過程就完成了!