Android Service演义
(本文以Android 5.1為準(zhǔn))
1.概述
在Android平臺(tái)上,那種持續(xù)性工作一般都是由service來(lái)執(zhí)行的。不少初學(xué)者總是搞不清service和線程、進(jìn)程之間的關(guān)系,這當(dāng)然會(huì)影響到他們開(kāi)展具體的開(kāi)發(fā)工作。
其實(shí),簡(jiǎn)單說(shuō)起來(lái),service和線程、進(jìn)程是沒(méi)什么關(guān)系的。我們知道,在Android平臺(tái)上已經(jīng)大幅度地弱化了進(jìn)程的概念,取而代之的是一個(gè)個(gè)有意義的邏輯實(shí)體,比如activity、service等。Service實(shí)體必然要寄身到某個(gè)進(jìn)程里才行,它也可以再啟動(dòng)幾個(gè)線程來(lái)幫它干活兒。但是,說(shuō)到底service只是一個(gè)邏輯實(shí)體、一個(gè)運(yùn)行期上下文而已。
相比activity這種“操控UI界面的運(yùn)行期上下文”,service這種上下文一般是沒(méi)有界面部分的。當(dāng)然這里說(shuō)的只是一般情況,有些特殊的service還是可以創(chuàng)建自己的界面的,比如當(dāng)一個(gè)service需要顯現(xiàn)某種浮動(dòng)面板時(shí),就必須自己創(chuàng)建、銷毀界面了。
在Android系統(tǒng)內(nèi)部的AMS里,是利用各種類型的Record節(jié)點(diǎn)來(lái)管理不同的運(yùn)行期上下文的。比如以ActivityRecord來(lái)管理activity,以ServiceRecord來(lái)管理service。可是,線程這種東東可沒(méi)有對(duì)應(yīng)的Record節(jié)點(diǎn)喔。一些初學(xué)者常常會(huì)在activity里啟動(dòng)一個(gè)線程,從事某種耗時(shí)費(fèi)力的工作,可是一旦activity被遮擋住,天知道它會(huì)在什么時(shí)候被系統(tǒng)砍掉,進(jìn)而導(dǎo)致連應(yīng)用進(jìn)程也退出。從AMS的角度來(lái)看,它壓根就不知道用戶進(jìn)程里還搞了個(gè)工作線程在干活兒,所以當(dāng)它要干掉用戶進(jìn)程時(shí),是不會(huì)考慮用戶進(jìn)程里還有沒(méi)有工作沒(méi)干完。
但如果是在service里啟動(dòng)了工作線程,那么AMS一般是不會(huì)隨便砍掉service所在的進(jìn)程的,所以耗時(shí)的工作也就可以順利進(jìn)行了。
可是,我們也常常遇到那種“一次性處理”的工作,難道就不能臨時(shí)創(chuàng)建個(gè)線程來(lái)干活嗎?關(guān)于這一點(diǎn),請(qǐng)大家留意,在Android平臺(tái)上應(yīng)對(duì)這種情況的較好做法是創(chuàng)建一個(gè)IntentService派生類,而后覆蓋其onHandleIntent()成員函數(shù)。IntentService內(nèi)部會(huì)自動(dòng)為你啟動(dòng)一個(gè)工作線程,并在工作線程里回調(diào)onHandleIntent()。當(dāng)onHandleIntent()返回后,IntentService還會(huì)自動(dòng)執(zhí)行stopSelf()關(guān)閉自己。瞧瞧,多么自動(dòng)化。
關(guān)于service,有兩個(gè)動(dòng)作是誰(shuí)都繞不開(kāi)的,那就是startService()和bindService()。我們想知道,它們的內(nèi)部機(jī)制到底是怎樣的?雖然網(wǎng)上已經(jīng)有不少文章講述過(guò)這兩個(gè)動(dòng)作,但是不同人理解技術(shù)的視角往往是不一樣的,本文我將以自己的視角來(lái)闡述它們。
?
2.Service機(jī)制
我們先來(lái)看一下Service類的代碼截選:
【frameworks/base/core/java/android/app/Service.java】
Service是個(gè)抽象類,它間接繼承于Context,其繼承關(guān)系如下圖所示:
看了這張圖,請(qǐng)大家務(wù)必理解,Service只是個(gè)“上下文”(Context)對(duì)象而已,它和進(jìn)程、線程是沒(méi)什么關(guān)系的。
在AMS中,負(fù)責(zé)管理service的ServiceRecord節(jié)點(diǎn)本身就是個(gè)binder實(shí)體。當(dāng)AMS向應(yīng)用進(jìn)程發(fā)出語(yǔ)義,要求其創(chuàng)建service對(duì)象時(shí),會(huì)把ServiceRecord通過(guò)binder機(jī)制“傳遞”給應(yīng)用進(jìn)程。這樣,應(yīng)用進(jìn)程的ActivityThread在處理AMS發(fā)來(lái)的語(yǔ)義時(shí),就可以得到一個(gè)合法的binder代理,這個(gè)binder代理最終會(huì)被記錄在Service對(duì)象中,如此一來(lái),Service實(shí)體就和系統(tǒng)里的ServiceRecord關(guān)聯(lián)起來(lái)了。我們畫個(gè)圖來(lái)說(shuō)明,假如一個(gè)應(yīng)用進(jìn)程里啟動(dòng)了兩個(gè)不同的Service,那么當(dāng)service創(chuàng)建成功之后,AMS和用戶進(jìn)程之間就會(huì)形成如下關(guān)系示意圖:
當(dāng)然,如果用戶進(jìn)程和service再多一點(diǎn)兒也完全沒(méi)問(wèn)題,此時(shí)會(huì)形成下圖:
我們對(duì)圖中的ApplicationThread并不陌生,它記錄在ActivityThread中mAppThread域中。每當(dāng)系統(tǒng)新fork一個(gè)用戶進(jìn)程后,就會(huì)自動(dòng)執(zhí)行ActivityThread的attach()動(dòng)作,里面會(huì)調(diào)用:
final IActivityManager mgr = ActivityManagerNative.getDefault(); . . . . . .mgr.attachApplication(mAppThread); . . . . . .將ApplicationThread對(duì)象遠(yuǎn)程“傳遞”給AMS,從而讓AMS得到一個(gè)合法的代理端。而當(dāng)系統(tǒng)要求用戶進(jìn)程創(chuàng)建service時(shí),就會(huì)通過(guò)這個(gè)合法的代理端向用戶進(jìn)程傳遞明確的語(yǔ)義。現(xiàn)在我們就從這里開(kāi)始講解細(xì)節(jié)吧。
3.先說(shuō)startService()
我們先來(lái)看啟動(dòng)service的流程。要啟動(dòng)一個(gè)service,我們一般是調(diào)用startService()。說(shuō)穿了只是向AMS發(fā)起一個(gè)請(qǐng)求,導(dǎo)致AMS執(zhí)行如下的startService()動(dòng)作:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】
其中的mServices域是ActiveServices類型的,其startServiceLocked()函數(shù)的代碼截選如下:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
看到了嗎?必須先通過(guò)retrieveServiceLocked()找到(或創(chuàng)建)一個(gè)ServiceRecord節(jié)點(diǎn),而后才能執(zhí)行后續(xù)的啟動(dòng)service的動(dòng)作。請(qǐng)大家注意,在Android frameworks里有不少這樣的動(dòng)作,基本上都是先創(chuàng)建xxxRecord節(jié)點(diǎn),而后再向目標(biāo)用戶進(jìn)程傳遞語(yǔ)義,創(chuàng)建具體的對(duì)象。
retrieveServiceLocked()的代碼截選如下:
private ServiceLookupResult retrieveServiceLocked(Intent service, ? ? ? ? String resolvedType, int callingPid, int callingUid, int userId, ? ? ? ? boolean createIfNeeded, boolean callingFromFg) {ServiceRecord r = null;. . . . . .ServiceMap smap = getServiceMap(userId);final ComponentName comp = service.getComponent();if (comp != null) {r = smap.mServicesByName.get(comp);}if (r == null) {Intent.FilterComparison filter = new Intent.FilterComparison(service);r = smap.mServicesByIntent.get(filter);}if (r == null) {. . . . . .// 從PKMS處查到ServiceInfo. . . . . .ComponentName name = new ComponentName(sInfo.applicationInfo.packageName, sInfo.name);. . . . . .r = smap.mServicesByName.get(name);if (r == null && createIfNeeded) {. . . . . .r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);. . . . . .smap.mServicesByName.put(name, r);smap.mServicesByIntent.put(filter, r);. . . . . .}. . . . . .}if (r != null) {. . . . . .return new ServiceLookupResult(r, null);}return null; }總之就是希望在AMS內(nèi)部的相關(guān)表格里找到對(duì)應(yīng)的ServiceRecord節(jié)點(diǎn),如果找不到,就創(chuàng)建一個(gè)新節(jié)點(diǎn),并插入到相應(yīng)的表格中。
我手頭參考的是Android5.1的代碼,可以說(shuō)這張表格大體上就是ServiceMap里的mServicesByName啦。當(dāng)然,ServiceRecord節(jié)點(diǎn)一般還會(huì)同時(shí)記錄到其他表格里,比如mServicesByIntent表格,但現(xiàn)在我們不妨先只考慮mServicesByName,一切以便于理解為上。
事實(shí)上,在早期的Android代碼(比如Android 2.3版的代碼)中,是沒(méi)有那個(gè)ServiceMap的,那時(shí)的mServicesByName表格直接位于ActivityManagerService里,而且對(duì)應(yīng)的域名叫作mServices。后來(lái)隨著Android的發(fā)展,需要支持多用戶以及其他一些概念,于是就搞出了個(gè)ServiceMap,并把原來(lái)的這張ServiceRecord表格搬到ServiceMap里了。我們可以畫一張示意圖,來(lái)說(shuō)明Android代碼的變遷:
(Android 2.3版示意圖)
(Android 5.1版示意圖)
但是,不管ServiceRecord表格被放到哪里,其本質(zhì)都是一致的。而AMS必須保證在實(shí)際啟動(dòng)一個(gè)Service之前查到或創(chuàng)建對(duì)應(yīng)的ServiceRecord節(jié)點(diǎn)。
在ServiceRecord類中有不少成員變量,其中有一個(gè)app域,專門用來(lái)記錄Service對(duì)應(yīng)的用戶進(jìn)程的ProcessRecord。當(dāng)然,一開(kāi)始這個(gè)app域是為null的,也就是說(shuō)ServiceRecord節(jié)點(diǎn)還沒(méi)有和用戶進(jìn)程關(guān)聯(lián)起來(lái),待Service真正啟動(dòng)之后,ServiceRecord的app域就有實(shí)際的值了。
現(xiàn)在我們繼續(xù)看startServiceLocked()函數(shù),它在找到ServiceRecord節(jié)點(diǎn)之后,開(kāi)始調(diào)用startServiceInnerLocked()。我們可以繪制一下相關(guān)的調(diào)用關(guān)系:
請(qǐng)注意上圖中bringUpServiceLocked()里的內(nèi)容。此時(shí)大體上分三種情況:
1)如果ServiceRecord已經(jīng)和Service寄身的用戶進(jìn)程關(guān)聯(lián)起來(lái)了,此時(shí)ServiceRecord的app域以及app.thread域都是有值的,那么只調(diào)用 sendServiceArgsLocked()即可。這一步會(huì)讓Service間接走到大家熟悉的onStartCommand()。
2)如果尚未啟動(dòng)Service寄身的用戶進(jìn)程,那么需要調(diào)用mAm.startProcessLocked()啟動(dòng)用戶進(jìn)程。
3)如果Service寄身的用戶進(jìn)程已經(jīng)啟動(dòng),但尚未和ServiceRecord關(guān)聯(lián)起來(lái),那么調(diào)用realStartServiceLocked(r, app, execInFg);這種情況下,會(huì)讓Service先走到onCreate(),而后再走到onStartCommand()。
我們重點(diǎn)看第三種情況,realStartServiceLocked()的調(diào)用示意圖如下:
看到了吧,無(wú)非是利用scheduleXXX()這樣的函數(shù),來(lái)通知用戶進(jìn)程去做什么事。以后大家看到這種以schedule打頭的函數(shù),可以直接打開(kāi)ActivityThread.java文件去查找其對(duì)應(yīng)的實(shí)現(xiàn)函數(shù)。比如scheduleCreateService()的代碼如下:
【frameworks/base/core/java/android/app/ActivityThread.java】
它發(fā)出的CREATE_SERVICE消息,會(huì)由ActivityThread的內(nèi)嵌類H處理,H繼承于Handler,而且是在UI主線程里處理消息的。可以看到,處理CREATE_SERVICE消息的函數(shù)是handleCreateService():
case CREATE_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");handleCreateService((CreateServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;另外,請(qǐng)大家注意realStartServiceLocked()傳給scheduleCreateService()函數(shù)的第一個(gè)參數(shù)就是ServiceRecord類型的r,而ServiceRecord本身是個(gè)Binder實(shí)體噢。待“傳到”應(yīng)用進(jìn)程時(shí),這個(gè)Binder實(shí)體對(duì)應(yīng)的Binder代理被稱作token,記在了CreateServiceData對(duì)象的token域中。現(xiàn)在CreateServiceData對(duì)象又經(jīng)由msg.obj傳遞到消息處理函數(shù)里,并進(jìn)一步作為參數(shù)傳遞給handleCreateService()函數(shù)。
handleCreateService()的代碼如下:
private void handleCreateService(CreateServiceData data) {. . . . . .LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Service service = null;. . . . . .java.lang.ClassLoader cl = packageInfo.getClassLoader();service = (Service) cl.loadClass(data.info.name).newInstance();. . . . . .ContextImpl context = ContextImpl.createAppContext(this, packageInfo);context.setOuterContext(service);Application app = packageInfo.makeApplication(false, mInstrumentation);// 注意,ServiceRecord實(shí)體對(duì)應(yīng)的代理端,就是此處的data.token。service.attach(context, this, data.info.name, data.token, app,ActivityManagerNative.getDefault());service.onCreate();mServices.put(data.token, service);. . . . . . }簡(jiǎn)單地說(shuō)就是,先利用反射機(jī)制創(chuàng)建出Service對(duì)象:
service = (Service) cl.loadClass(data.info.name).newInstance();然后再調(diào)用該對(duì)象的onCreate()成員函數(shù):
service.onCreate();而因?yàn)閔andleCreateService()函數(shù)本身是在用戶進(jìn)程的UI主線程里執(zhí)行的,所以service的onCreate()函數(shù)也就是在UI主線程里執(zhí)行的。常常會(huì)有人告誡新手,不要在onCreate()、onStart()里執(zhí)行耗時(shí)的操作,現(xiàn)在大家知道是為什么了吧,因?yàn)樵赨I主線程里執(zhí)行耗時(shí)的操作不但會(huì)引起界面卡頓,嚴(yán)重的還會(huì)導(dǎo)致ANR報(bào)錯(cuò)。
另外,在執(zhí)行到上面的service.attach()時(shí),那個(gè)和ServiceRecord對(duì)應(yīng)的代理端token也傳進(jìn)來(lái)了:
public final void attach( ? ? ? ? Context context, ? ? ? ? ActivityThread thread, String className, IBinder token, ? ? ? ? Application application, Object activityManager) {attachBaseContext(context);mThread = thread; ? ? ? ? ??mClassName = className;mToken = token; ? ? // 注意這個(gè)token噢,它的對(duì)端就是AMS里的ServiceRecord!mApplication = application;mActivityManager = (IActivityManager)activityManager;mStartCompatibility = getApplicationInfo().targetSdkVersion< Build.VERSION_CODES.ECLAIR; }可以看到,token記錄到ServiceRecord的mToken域了。
最后,新創(chuàng)建出的Service對(duì)象還會(huì)記錄進(jìn)ActivityThread的mServices表格去,于是我們可以在前文示意圖的基礎(chǔ)上,再畫一張新圖:
這就是startService()啟動(dòng)服務(wù)的大體過(guò)程。上圖并沒(méi)有繪制“調(diào)用startService()的用戶進(jìn)程”,因?yàn)楫?dāng)Service啟動(dòng)后,它基本上就和發(fā)起方?jīng)]什么關(guān)系了。
?
4.再說(shuō)bindService()
接下來(lái)我們來(lái)說(shuō)說(shuō)bindService(),它可比startService()要麻煩一些。當(dāng)一個(gè)用戶進(jìn)程bindService()時(shí),它需要先準(zhǔn)備好一個(gè)實(shí)現(xiàn)了ServiceConnection接口的對(duì)象。ServiceConnection的定義如下:
public interface ServiceConnection {public void onServiceConnected(ComponentName name, IBinder service);public void onServiceDisconnected(ComponentName name); }在Android平臺(tái)上,每當(dāng)用戶調(diào)用bindService(),Android都將之視作是要建立一個(gè)新的“邏輯連接”。而當(dāng)連接建立起來(lái)時(shí),系統(tǒng)會(huì)回調(diào)ServiceConnection接口的onServiceConnected()。另一方面,那個(gè)onServiceDisconnected()函數(shù)卻不是在unbindService()時(shí)發(fā)生的。一般來(lái)說(shuō),當(dāng)目標(biāo)service所在的進(jìn)程意外掛掉或者被殺掉時(shí),系統(tǒng)才會(huì)回調(diào)onServiceDisconnected(),而且,此時(shí)并不會(huì)銷毀之前的邏輯連接,也就是說(shuō),那個(gè)“邏輯連接”仍然處于激活狀態(tài),一旦service后續(xù)再次運(yùn)行,系統(tǒng)會(huì)再次回調(diào)onServiceConnected()。
在Android平臺(tái)上,要和其他進(jìn)程建立邏輯連接往往都需要利用binder機(jī)制。那么,發(fā)起bindService()的用戶進(jìn)程又是在哪里創(chuàng)建邏輯連接需要的binder實(shí)體呢?我們可以看看bindServiceCommon()函數(shù)的代碼截選:
【frameworks/base/core/java/android/app/ContextImpl.java】
請(qǐng)大家注意mPackageInfo.getServiceDispatcher()那一句,它返回的就是binder實(shí)體。此處的mPackageInfo是用戶進(jìn)程里和apk對(duì)應(yīng)的LoadedApk對(duì)象。getServiceDispatcher()的代碼如下:
【frameworks/base/core/java/android/app/LoadedApk.java】
也就是說(shuō),先嘗試在LoadedApk的mServices表中查詢ServiceDispatcher對(duì)象,如果查不到,就重新創(chuàng)建一個(gè)。ServiceDispatcher對(duì)象會(huì)記錄下從用戶處傳來(lái)的ServiceConnection引用。而且,ServiceDispatcher對(duì)象內(nèi)部還含有一個(gè)binder實(shí)體,現(xiàn)在我們可以通過(guò)調(diào)用sd.getIServiceConnection()一句,返回這個(gè)內(nèi)部的binder實(shí)體。
現(xiàn)在我們畫一張示意圖,來(lái)說(shuō)明發(fā)起bindService()動(dòng)作的用戶進(jìn)程中的樣子:
LoadedApk中的mServices是常見(jiàn)的表中表形式,定義如下:
private final ArrayMap<Context, ArrayMap<ServiceConnection,?LoadedApk.ServiceDispatcher>> mServices這里比較有趣的是,第一層表的key類型為Context,大家不妨想一想,如果我們的應(yīng)用里有兩個(gè)activity都去綁定同一個(gè)Service會(huì)怎么樣?很明顯,在mServices表里就會(huì)有兩個(gè)不同的子表,也會(huì)創(chuàng)建出兩個(gè)不同的ServiceDispatcher對(duì)象,而且即便我們?cè)谡{(diào)用bindService()時(shí)傳入同一個(gè)ServiceConnection對(duì)象,依舊會(huì)有兩個(gè)ServiceDispatcher對(duì)象。示意圖如下:
說(shuō)完了發(fā)起bindService()動(dòng)作的用戶進(jìn)程一側(cè),接下來(lái)我們?cè)賮?lái)看AMS一側(cè)的代碼。對(duì)應(yīng)的bindService()代碼如下:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】
主要工作集中在mServices.bindServiceLocked()一句。請(qǐng)注意bindService()的倒數(shù)第三個(gè)參數(shù),它對(duì)應(yīng)的就是上圖中ServiceDispatcher的mIServiceConnection域記錄的binder實(shí)體。至于bindService()的第二個(gè)參數(shù)IBinder token,其實(shí)記錄的是發(fā)起綁定動(dòng)作的Activity的token,當(dāng)然,如果發(fā)起者是其他非Activity型的對(duì)象,那么這個(gè)參數(shù)應(yīng)該是null。
上面的代碼最終走到mServices.bindServiceLocked()一句。我們摘錄一下bindServiceLocked()的代碼:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
盡管這個(gè)函數(shù)里有不少技術(shù)細(xì)節(jié),而且涉及到多個(gè)映射表,但它的總體意思大概是這樣的。每當(dāng)用戶調(diào)用bindService()時(shí),Android都將之看作是要建立一個(gè)新的“邏輯連接”,而每個(gè)邏輯連接都對(duì)應(yīng)一個(gè)ConnectionRecord節(jié)點(diǎn),所以最終的表現(xiàn)肯定是向ServiceRecord內(nèi)部的某張映射表里添加一個(gè)新的ConnectionRecord節(jié)點(diǎn)。當(dāng)然,在實(shí)際運(yùn)作時(shí),這個(gè)節(jié)點(diǎn)還會(huì)記錄進(jìn)其他幾個(gè)映射表里(比如系統(tǒng)總映射表),但這不影響我們理解問(wèn)題。
那么為什么系統(tǒng)里會(huì)有那么多映射表呢?這大概是為了在復(fù)雜的網(wǎng)狀聯(lián)系中快速便捷地找到相關(guān)的節(jié)點(diǎn)。我們知道,一個(gè)用戶進(jìn)程可以綁定多個(gè)Service,而一個(gè)Service也可以被多個(gè)用戶進(jìn)程綁定,這就是網(wǎng)狀結(jié)構(gòu)。示意圖如下:
前文我們已經(jīng)說(shuō)過(guò),綁定時(shí)使用的ServiceConnection對(duì)象在發(fā)起端其實(shí)對(duì)應(yīng)了一個(gè)ServiceDispatcher對(duì)象,現(xiàn)在,ServiceDispatcher的mIServiceConnection傳遞給bindServiceLocked(),也就是那個(gè)IServiceConnection connection參數(shù)。如果系統(tǒng)要建立“邏輯連接”,那么就需要把IServiceConnection代理端記錄到ConnectionRecord節(jié)點(diǎn)里。
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags,?clientLabel, clientIntent);另外,請(qǐng)大家注意bindServiceLocked()里那個(gè)重要的AppBindRecord節(jié)點(diǎn)。
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ?// 注意這個(gè)b!相傳對(duì)于一個(gè)Service而言,有多少應(yīng)用和它建立了綁定關(guān)系,就會(huì)有多少個(gè)AppBindRecord節(jié)點(diǎn),要不怎么叫App-Bind呢?當(dāng)然,一個(gè)應(yīng)用里可以有多個(gè)地方發(fā)起綁定動(dòng)作,所以AppBindRecord里需要用一個(gè)ArraySet<ConnectionRecord>記錄下每個(gè)綁定動(dòng)作對(duì)應(yīng)的邏輯連接節(jié)點(diǎn)。
有了這些認(rèn)識(shí)后,我們可以畫一張“發(fā)起端用戶進(jìn)程”和“系統(tǒng)進(jìn)程”之間的示意圖:
在ConnectionRecord被記錄進(jìn)合適的表后,要開(kāi)始和目標(biāo)service建立連接了。我們可以看到,bindServiceLocked()會(huì)嘗試呼叫起目標(biāo)service。
看到這句if語(yǔ)句,大家應(yīng)該知道調(diào)用bindService()時(shí)為什么常常要加上BIND_AUTO_CREATE了吧:
bindService(intent, conn, Context.BIND_AUTO_CREATE);?
假設(shè)目標(biāo)Service之前已經(jīng)啟動(dòng)過(guò),那么現(xiàn)在的綁定流程會(huì)走到if (s.app != null && b.intent.received)分支里,于是調(diào)用到c.conn.connected()。
? ? ? ? if (s.app != null && b.intent.received) {. . . . . .c.conn.connected(s.name, b.intent.binder); ?// 注意這個(gè)!. . . . . .if (b.intent.apps.size() == 1 && b.intent.doRebind) {requestServiceBindingLocked(s, b.intent, callerFg, true);}} else if (!b.intent.requested) {requestServiceBindingLocked(s, b.intent, callerFg, false); ?}但是,如果Service之前并未啟動(dòng),而且這次是我們首次綁定service,那么不就到不了c.conn.connected()了嗎?此時(shí)應(yīng)該會(huì)走到else if 分支,調(diào)用到requestServiceBindingLocked(),該函數(shù)主要是向目標(biāo)service發(fā)起綁定的請(qǐng)求,但是現(xiàn)在連service寄身的進(jìn)程可能都還沒(méi)有啟動(dòng),request又有什么意義呢?所以,此處調(diào)用requestServiceBindingLocked()也許不會(huì)有什么重大意義。這并不是說(shuō)requestServiceBindingLocked()不重要,而是說(shuō)它真正起作用的地方也許不在這里。為了說(shuō)明問(wèn)題,我們得看一下剛剛調(diào)用的bringUpServiceLocked()函數(shù):
private final String bringUpServiceLocked(ServiceRecord r, ? ? ? ? int intentFlags, boolean execInFg, boolean whileRestarting) {. . . . . .ProcessRecord app;. . . . . .if (app == null) {if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,"service", r.name, false, isolated, false)) == null) {. . . . . .bringDownServiceLocked(r);return msg;}. . . . . .}// 注意此處的mPendingServices!if (!mPendingServices.contains(r)) {mPendingServices.add(r);}. . . . . . }bringUpServiceLocked()通過(guò)調(diào)用mAm.startProcessLocked()啟動(dòng)目標(biāo)service寄身的進(jìn)程,而后會(huì)把ServiceRecord節(jié)點(diǎn)記入mPendingServices數(shù)組列表中。
待后續(xù)service寄身的進(jìn)程成功啟動(dòng)后,會(huì)輾轉(zhuǎn)調(diào)用到attachApplicationLocked(),該函數(shù)的代碼截選如下:
boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException {. . . . . .if (mPendingServices.size() > 0) {ServiceRecord sr = null;. . . . . .for (int i=0; i<mPendingServices.size(); i++) {sr = mPendingServices.get(i);. . . . . .mPendingServices.remove(i);i--;proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode, mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg);. . . . . .}. . . . . . }也就是說(shuō),當(dāng)目標(biāo)service寄身的進(jìn)程啟動(dòng)后,會(huì)從mPendingServices數(shù)組列表中把ServiceRecord節(jié)點(diǎn)刪除,并進(jìn)一步調(diào)用realStartServiceLocked():
private final void realStartServiceLocked(ServiceRecord r, ? ? ? ? ProcessRecord app, boolean execInFg) throws RemoteException {. . . . . .r.app = app;. . . . . .app.services.add(r);. . . . . .app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);. . . . . .requestServiceBindingsLocked(r, execInFg);. . . . . .sendServiceArgsLocked(r, execInFg, true);. . . . . . }此處調(diào)用的app.thread.scheduleCreateService()會(huì)間接導(dǎo)致目標(biāo)service走到大家熟悉的onCreate()。而后還會(huì)調(diào)用requestServiceBindingsLocked()。這里大概才是requestServiceBindingLocked()真正起作用的地方。
requestServiceBindingsLocked()的代碼如下:
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) {for (int i=r.bindings.size()-1; i>=0; i--) {IntentBindRecord ibr = r.bindings.valueAt(i);if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {break;}} } private final boolean requestServiceBindingLocked(ServiceRecord r, ? ? ? ? IntentBindRecord i, boolean execInFg, boolean rebind) {. . . . . .r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,r.app.repProcState);if (!rebind) {i.requested = true;}i.hasBound = true;i.doRebind = false;. . . . . . }終于看到調(diào)用scheduleBindService()了。
到了“Service所屬的進(jìn)程”里,scheduleBindService()執(zhí)行的代碼如下:
public final void scheduleBindService(IBinder token, Intent intent, ? ? ? ? boolean rebind, int processState) {updateProcessState(processState, false);BindServiceData s = new BindServiceData();s.token = token;?? ??? ?// 對(duì)應(yīng)AMS里的ServiceRecords.intent = intent;s.rebind = rebind;......sendMessage(H.BIND_SERVICE, s); }?
所發(fā)出的BIND_SERVICE消息,會(huì)導(dǎo)致service寄身的進(jìn)程走到handleBindService():
【frameworks/base/core/java/android/app/ActivityThread.java】
回調(diào)了目標(biāo)service的onBind(),而后向AMS發(fā)布自己,即調(diào)用publishService()。
publishService()的第一個(gè)參數(shù)指代AMS里的ServiceRecord節(jié)點(diǎn),而最后一個(gè)參數(shù)是目標(biāo)Service的onBind()函數(shù)返回的服務(wù)對(duì)象。因此publish函數(shù)其實(shí)起的是銜接的作用。
在AMS一側(cè),publish動(dòng)作最終會(huì)走到publishServiceLocked():
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
這里又牽扯到ServiceRecord的connections映射表,在介紹bindServiceLocked()函數(shù)時(shí),我們其實(shí)已經(jīng)看到過(guò)幾句相關(guān)的代碼了,現(xiàn)在再列舉一下:
? ? ? ? IBinder binder = connection.asBinder();ArrayList<ConnectionRecord> clist = s.connections.get(binder);. . . . . .clist.add(c);也就是說(shuō),我們?cè)诮壎ǚ?wù)時(shí)創(chuàng)建的那個(gè)ConnectionRecord節(jié)點(diǎn),會(huì)同時(shí)將該節(jié)點(diǎn)記錄進(jìn)ServiceRecord的connections映射表,而映射表的key值就是邏輯上指向發(fā)起端的IServiceConnection代理。
為什么要有這個(gè)映射表呢?很簡(jiǎn)單,就是為了快速地找出所有使用相同IServiceConnection.Stub對(duì)象,完成綁定動(dòng)作的ConnectionRecord節(jié)點(diǎn)。請(qǐng)大家設(shè)想,我們完全可以在一個(gè)Activity里,使用同一個(gè)ServiceConnection對(duì)象發(fā)起多次綁定同一個(gè)service的動(dòng)作,發(fā)起綁定時(shí)使用的intent也許會(huì)不同,但最終有可能綁定到同一個(gè)service,此時(shí),上面代碼中s.connections.get(binder)得到的ArrayList就會(huì)含有多個(gè)元素啦。
好,介紹完connections映射表,我們繼續(xù)看publishServiceLocked()里的那句c.conn.connected()。ConnectionRecord節(jié)點(diǎn)的conn成員也是IServiceConnection類型的代理端,所以這一句調(diào)用最終是遠(yuǎn)程調(diào)用到發(fā)起端的ServiceDispatcher里的mIServiceConnection對(duì)象,該對(duì)象是InnerConnection類型的。
InnerConnection的connected()函數(shù)如下:
【frameworks/base/core/java/android/app/LoadedApk.java】
而ServiceDispatcher的connected()函數(shù)如下:
public void connected(ComponentName name, IBinder service) {if (mActivityThread != null) {mActivityThread.post(new RunConnection(name, service, 0));} else {doConnected(name, service);} } private final class RunConnection implements Runnable {. . . . . .public void run() {if (mCommand == 0) {doConnected(mName, mService);} else if (mCommand == 1) {doDeath(mName, mService);}}. . . . . . }?
【frameworks/base/core/java/android/app/LoadedApk.java】
public void doConnected(ComponentName name, IBinder service) {. . . . . .info = new ConnectionInfo();info.binder = service;info.deathMonitor = new DeathMonitor(name, service);try {service.linkToDeath(info.deathMonitor, 0);mActiveConnections.put(name, info);} catch (RemoteException e) {. . . . . .}. . . . . .// If there is a new service, it is now connected.if (service != null) {mConnection.onServiceConnected(name, service); ? // 終于看到onServiceConnected了!} }
至此,我們終于看到大家熟悉的onServiceConnected()回調(diào)啦!而傳來(lái)的service參數(shù),就是我們希望綁定的那個(gè)service提供的binder代理。現(xiàn)在我們可以說(shuō)已經(jīng)打通了bindService()動(dòng)作涉及的三方關(guān)系:發(fā)起方、AMS、目標(biāo)Service。我們不妨再畫一張圖看看:
有關(guān)Service的基本機(jī)制和啟動(dòng)流程,我們就先說(shuō)這么多吧。以后我們?cè)傺a(bǔ)充其他方面的內(nèi)容。
原文地址:?https://my.oschina.net/youranhongcha/blog/710046
總結(jié)
以上是生活随笔為你收集整理的Android Service演义的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 品茗论道说广播(Broadcast内部机
- 下一篇: 深入讲解Android Property