Android Service演義
(本文以Android 5.1爲準)
1.概述
在Android平臺上,那種持續性工作一般都是由service來執行的。不少初學者總是搞不清service和線程、進程之間的關係,這當然會影響到他們開展具體的開發工作。
其實,簡單說起來,service和線程、進程是沒什麼關係的。我們知道,在Android平臺上已經大幅度地弱化了進程的概念,取而代之的是一個個有意義的邏輯實體,比如activity、service等。Service實體必然要寄身到某個進程裏才行,它也可以再啓動幾個線程來幫它幹活兒。但是,說到底service只是一個邏輯實體、一個運行期上下文而已。
相比activity這種“操控UI界面的運行期上下文”,service這種上下文一般是沒有界面部分的。當然這裏說的只是一般情況,有些特殊的service還是可以創建自己的界面的,比如當一個service需要顯現某種浮動面板時,就必須自己創建、銷燬界面了。
在Android系統內部的AMS裏,是利用各種類型的Record節點來管理不同的運行期上下文的。比如以ActivityRecord來管理activity,以ServiceRecord來管理service。可是,線程這種東東可沒有對應的Record節點喔。一些初學者常常會在activity裏啓動一個線程,從事某種耗時費力的工作,可是一旦activity被遮擋住,天知道它會在什麼時候被系統砍掉,進而導致連應用進程也退出。從AMS的角度來看,它壓根就不知道用戶進程裏還搞了個工作線程在幹活兒,所以當它要幹掉用戶進程時,是不會考慮用戶進程裏還有沒有工作沒幹完。
但如果是在service裏啓動了工作線程,那麼AMS一般是不會隨便砍掉service所在的進程的,所以耗時的工作也就可以順利進行了。
可是,我們也常常遇到那種“一次性處理”的工作,難道就不能臨時創建個線程來幹活嗎?關於這一點,請大家留意,在Android平臺上應對這種情況的較好做法是創建一個IntentService派生類,而後覆蓋其onHandleIntent()成員函數。IntentService內部會自動爲你啓動一個工作線程,並在工作線程裏回調onHandleIntent()。當onHandleIntent()返回後,IntentService還會自動執行stopSelf()關閉自己。瞧瞧,多麼自動化。
關於service,有兩個動作是誰都繞不開的,那就是startService()和bindService()。我們想知道,它們的內部機制到底是怎樣的?雖然網上已經有不少文章講述過這兩個動作,但是不同人理解技術的視角往往是不一樣的,本文我將以自己的視角來闡述它們。
2.Service機制
我們先來看一下Service類的代碼截選:
【frameworks/base/core/Java/android/app/Service.java】
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
......
......
private ActivityThread mThread = null;
private String mClassName = null;
private IBinder mToken = null;
private Application mApplication = null;
private IActivityManager mActivityManager = null;
private boolean mStartCompatibility = false;
}
Service是個抽象類,它間接繼承於Context,其繼承關係如下圖所示:
看了這張圖,請大家務必理解,Service只是個“上下文”(Context)對象而已,它和進程、線程是沒什麼關係的。
在AMS中,負責管理service的ServiceRecord節點本身就是個binder實體。當AMS嚮應用進程發出語義,要求其創建service對象時,會把ServiceRecord通過binder機制“傳遞”給應用進程。這樣,應用進程的ActivityThread在處理AMS發來的語義時,就可以得到一個合法的binder代理,這個binder代理最終會被記錄在Service對象中,如此一來,Service實體就和系統裏的ServiceRecord關聯起來了。我們畫個圖來說明,假如一個應用進程裏啓動了兩個不同的Service,那麼當service創建成功之後,AMS和用戶進程之間就會形成如下關係示意圖:
當然,如果用戶進程和service再多一點兒也完全沒問題,此時會形成下圖:
我們對圖中的ApplicationThread並不陌生,它記錄在ActivityThread中mAppThread域中。每當系統新fork一個用戶進程後,就會自動執行ActivityThread的attach()動作,裏面會調用:
final IActivityManager mgr = ActivityManagerNative.getDefault();
. . . . . .
mgr.attachApplication(mAppThread);
. . . . . .
將ApplicationThread對象遠程“傳遞”給AMS,從而讓AMS得到一個合法的代理端。而當系統要求用戶進程創建service時,就會通過這個合法的代理端向用戶進程傳遞明確的語義。現在我們就從這裏開始講解細節吧。
3.先說startService()
我們先來看啓動service的流程。要啓動一個service,我們一般是調用startService()。說穿了只是向AMS發起一個請求,導致AMS執行如下的startService()動作:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
. . . . . .
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, userId);
. . . . . .
return res;
}
}
其中的mServices域是ActiveServices類型的,其startServiceLocked()函數的代碼截選如下:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
ComponentName startServiceLocked(IApplicationThread caller,
Intent service, String resolvedType,
int callingPid, int callingUid, int userId) {
. . . . . .
// 必須先通過retrieveServiceLocked()找到(或創建)一個ServiceRecord節點
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
callingPid, callingUid, userId, true, callerFg);
. . . . . .
ServiceRecord r = res.record;
. . . . . .
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
final ServiceMap smap = getServiceMap(r.userId);
. . . . . .
. . . . . .
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
看到了嗎?必須先通過retrieveServiceLocked()找到(或創建)一個ServiceRecord節點,而後才能執行後續的啓動service的動作。請大家注意,在Android frameworks裏有不少這樣的動作,基本上都是先創建xxxRecord節點,而後再向目標用戶進程傳遞語義,創建具體的對象。
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內部的相關表格裏找到對應的ServiceRecord節點,如果找不到,就創建一個新節點,並插入到相應的表格中。
我手頭參考的是Android5.1的代碼,可以說這張表格大體上就是ServiceMap裏的mServicesByName啦。當然,ServiceRecord節點一般還會同時記錄到其他表格裏,比如mServicesByIntent表格,但現在我們不妨先只考慮mServicesByName,一切以便於理解爲上。
事實上,在早期的Android代碼(比如Android 2.3版的代碼)中,是沒有那個ServiceMap的,那時的mServicesByName表格直接位於ActivityManagerService裏,而且對應的域名叫作mServices。後來隨着Android的發展,需要支持多用戶以及其他一些概念,於是就搞出了個ServiceMap,並把原來的這張ServiceRecord表格搬到ServiceMap裏了。我們可以畫一張示意圖,來說明Android代碼的變遷:
(Android 2.3版示意圖)
(Android 5.1版示意圖)
但是,不管ServiceRecord表格被放到哪裏,其本質都是一致的。而AMS必須保證在實際啓動一個Service之前查到或創建對應的ServiceRecord節點。
在ServiceRecord類中有不少成員變量,其中有一個app域,專門用來記錄Service對應的用戶進程的ProcessRecord。當然,一開始這個app域是爲null的,也就是說ServiceRecord節點還沒有和用戶進程關聯起來,待Service真正啓動之後,ServiceRecord的app域就有實際的值了。
現在我們繼續看startServiceLocked()函數,它在找到ServiceRecord節點之後,開始調用startServiceInnerLocked()。我們可以繪製一下相關的調用關係:
請注意上圖中bringUpServiceLocked()裏的內容。此時大體上分三種情況:
1)如果ServiceRecord已經和Service寄身的用戶進程關聯起來了,此時ServiceRecord的app域以及app.thread域都是有值的,那麼只調用 sendServiceArgsLocked()即可。這一步會讓Service間接走到大家熟悉的onStartCommand()。
2)如果尚未啓動Service寄身的用戶進程,那麼需要調用mAm.startProcessLocked()啓動用戶進程。
3)如果Service寄身的用戶進程已經啓動,但尚未和ServiceRecord關聯起來,那麼調用realStartServiceLocked(r, app, execInFg);這種情況下,會讓Service先走到onCreate(),而後再走到onStartCommand()。
我們重點看第三種情況,realStartServiceLocked()的調用示意圖如下:
看到了吧,無非是利用scheduleXXX()這樣的函數,來通知用戶進程去做什麼事。以後大家看到這種以schedule打頭的函數,可以直接打開ActivityThread.java文件去查找其對應的實現函數。比如scheduleCreateService()的代碼如下:
【frameworks/base/core/java/android/app/ActivityThread.java】
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
它發出的CREATE_SERVICE消息,會由ActivityThread的內嵌類H處理,H繼承於Handler,而且是在UI主線程裏處理消息的。可以看到,處理CREATE_SERVICE消息的函數是handleCreateService():
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
另外,請大家注意realStartServiceLocked()傳給scheduleCreateService()函數的第一個參數就是ServiceRecord類型的r,而ServiceRecord本身是個Binder實體噢。待“傳到”應用進程時,這個Binder實體對應的Binder代理被稱作token,記在了CreateServiceData對象的token域中。現在CreateServiceData對象又經由msg.obj傳遞到消息處理函數裏,並進一步作爲參數傳遞給handleCreateService()函數。
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實體對應的代理端,就是此處的data.token。
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
. . . . . .
}
簡單地說就是,先利用反射機制創建出Service對象:
service = (Service) cl.loadClass(data.info.name).newInstance();
然後再調用該對象的onCreate()成員函數:
service.onCreate();
而因爲handleCreateService()函數本身是在用戶進程的UI主線程裏執行的,所以service的onCreate()函數也就是在UI主線程裏執行的。常常會有人告誡新手,不要在onCreate()、onStart()裏執行耗時的操作,現在大家知道是爲什麼了吧,因爲在UI主線程裏執行耗時的操作不但會引起界面卡頓,嚴重的還會導致ANR報錯。
另外,在執行到上面的service.attach()時,那個和ServiceRecord對應的代理端token也傳進來了:
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
mThread = thread;
mClassName = className;
mToken = token; // 注意這個token噢,它的對端就是AMS裏的ServiceRecord!
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
可以看到,token記錄到ServiceRecord的mToken域了。
最後,新創建出的Service對象還會記錄進ActivityThread的mServices表格去,於是我們可以在前文示意圖的基礎上,再畫一張新圖:
這就是startService()啓動服務的大體過程。上圖並沒有繪製“調用startService()的用戶進程”,因爲當Service啓動後,它基本上就和發起方沒什麼關係了。
4.再說bindService()
接下來我們來說說bindService(),它可比startService()要麻煩一些。當一個用戶進程bindService()時,它需要先準備好一個實現了ServiceConnection接口的對象。ServiceConnection的定義如下:
public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}
在Android平臺上,每當用戶調用bindService(),Android都將之視作是要建立一個新的“邏輯連接”。而當連接建立起來時,系統會回調ServiceConnection接口的onServiceConnected()。另一方面,那個onServiceDisconnected()函數卻不是在unbindService()時發生的。一般來說,當目標service所在的進程意外掛掉或者被殺掉時,系統纔會回調onServiceDisconnected(),而且,此時並不會銷燬之前的邏輯連接,也就是說,那個“邏輯連接”仍然處於激活狀態,一旦service後續再次運行,系統會再次回調onServiceConnected()。
在Android平臺上,要和其他進程建立邏輯連接往往都需要利用binder機制。那麼,發起bindService()的用戶進程又是在哪裏創建邏輯連接需要的binder實體呢?我們可以看看bindServiceCommon()函數的代碼截選:
【frameworks/base/core/java/android/app/ContextImpl.java】
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
IServiceConnection sd;
. . . . . .
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags); // 請注意返回的sd!
. . . . . .
IBinder token = getActivityToken();
. . . . . .
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, user.getIdentifier());
. . . . . .
}
請大家注意mPackageInfo.getServiceDispatcher()那一句,它返回的就是binder實體。此處的mPackageInfo是用戶進程裏和apk對應的LoadedApk對象。getServiceDispatcher()的代碼如下:
【frameworks/base/core/java/android/app/LoadedApk.java】
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map =
mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection(); // 注意,返回ServiceDispatcher內的binder實體
}
}
也就是說,先嚐試在LoadedApk的mServices表中查詢ServiceDispatcher對象,如果查不到,就重新創建一個。ServiceDispatcher對象會記錄下從用戶處傳來的ServiceConnection引用。而且,ServiceDispatcher對象內部還含有一個binder實體,現在我們可以通過調用sd.getIServiceConnection()一句,返回這個內部的binder實體。
現在我們畫一張示意圖,來說明發起bindService()動作的用戶進程中的樣子:
LoadedApk中的mServices是常見的表中表形式,定義如下:
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
這裏比較有趣的是,第一層表的key類型爲Context,大家不妨想一想,如果我們的應用裏有兩個activity都去綁定同一個Service會怎麼樣?很明顯,在mServices表裏就會有兩個不同的子表,也會創建出兩個不同的ServiceDispatcher對象,而且即便我們在調用bindService()時傳入同一個ServiceConnection對象,依舊會有兩個ServiceDispatcher對象。示意圖如下:
說完了發起bindService()動作的用戶進程一側,接下來我們再來看AMS一側的代碼。對應的bindService()代碼如下:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service, resolvedType,
connection, flags, userId);
}
}
主要工作集中在mServices.bindServiceLocked()一句。請注意bindService()的倒數第三個參數,它對應的就是上圖中ServiceDispatcher的mIServiceConnection域記錄的binder實體。至於bindService()的第二個參數IBinder token,其實記錄的是發起綁定動作的Activity的token,當然,如果發起者是其他非Activity型的對象,那麼這個參數應該是null。
上面的代碼最終走到mServices.bindServiceLocked()一句。我們摘錄一下bindServiceLocked()的代碼:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
int bindServiceLocked(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
. . . . . .
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
. . . . . .
ActivityRecord activity = null;
. . . . . .
activity = ActivityRecord.isInStackLocked(token);
. . . . . .
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
. . . . . .
ServiceRecord s = res.record;
. . . . . .
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); // 注意這個b!
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
. . . . . .
clist.add(c);
b.connections.add(c);
. . . . . .
b.client.connections.add(c);
. . . . . .
clist = mServiceConnections.get(binder);
. . . . . .
clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
. . . . . .
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
. . . . . .
if (s.app != null && b.intent.received) {
. . . . . .
c.conn.connected(s.name, b.intent.binder); // 注意這個!
. . . . . .
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);
}
. . . . . .
}
儘管這個函數裏有不少技術細節,而且涉及到多個映射表,但它的總體意思大概是這樣的。每當用戶調用bindService()時,Android都將之看作是要建立一個新的“邏輯連接”,而每個邏輯連接都對應一個ConnectionRecord節點,所以最終的表現肯定是向ServiceRecord內部的某張映射表裏添加一個新的ConnectionRecord節點。當然,在實際運作時,這個節點還會記錄進其他幾個映射表裏(比如系統總映射表),但這不影響我們理解問題。
那麼爲什麼系統裏會有那麼多映射表呢?這大概是爲了在複雜的網狀聯繫中快速便捷地找到相關的節點。我們知道,一個用戶進程可以綁定多個Service,而一個Service也可以被多個用戶進程綁定,這就是網狀結構。示意圖如下:
前文我們已經說過,綁定時使用的ServiceConnection對象在發起端其實對應了一個ServiceDispatcher對象,現在,ServiceDispatcher的mIServiceConnection傳遞給bindServiceLocked(),也就是那個IServiceConnection connection參數。如果系統要建立“邏輯連接”,那麼就需要把IServiceConnection代理端記錄到ConnectionRecord節點裏。
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags,
clientLabel, clientIntent);
另外,請大家注意bindServiceLocked()裏那個重要的AppBindRecord節點。
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); // 注意這個b!
相傳對於一個Service而言,有多少應用和它建立了綁定關係,就會有多少個AppBindRecord節點,要不怎麼叫App-Bind呢?當然,一個應用裏可以有多個地方發起綁定動作,所以AppBindRecord裏需要用一個ArraySet<ConnectionRecord>記錄下每個綁定動作對應的邏輯連接節點。
有了這些認識後,我們可以畫一張“發起端用戶進程”和“系統進程”之間的示意圖:
在ConnectionRecord被記錄進合適的表後,要開始和目標service建立連接了。我們可以看到,bindServiceLocked()會嘗試呼叫起目標service。
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
. . . . . .
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
看到這句if語句,大家應該知道調用bindService()時爲什麼常常要加上BIND_AUTO_CREATE了吧:
bindService(intent, conn, Context.BIND_AUTO_CREATE);
假設目標Service之前已經啓動過,那麼現在的綁定流程會走到if (s.app != null && b.intent.received)分支裏,於是調用到c.conn.connected()。
if (s.app != null && b.intent.received) {
. . . . . .
c.conn.connected(s.name, b.intent.binder); // 注意這個!
. . . . . .
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之前並未啓動,而且這次是我們首次綁定service,那麼不就到不了c.conn.connected()了嗎?此時應該會走到else if 分支,調用到requestServiceBindingLocked(),該函數主要是向目標service發起綁定的請求,但是現在連service寄身的進程可能都還沒有啓動,request又有什麼意義呢?所以,此處調用requestServiceBindingLocked()也許不會有什麼重大意義。這並不是說requestServiceBindingLocked()不重要,而是說它真正起作用的地方也許不在這裏。爲了說明問題,我們得看一下剛剛調用的bringUpServiceLocked()函數:
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()通過調用mAm.startProcessLocked()啓動目標service寄身的進程,而後會把ServiceRecord節點記入mPendingServices數組列表中。
待後續service寄身的進程成功啓動後,會輾轉調用到attachApplicationLocked(),該函數的代碼截選如下:
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);
. . . . . .
}
. . . . . .
}
也就是說,當目標service寄身的進程啓動後,會從mPendingServices數組列表中把ServiceRecord節點刪除,並進一步調用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);
. . . . . .
}
此處調用的app.thread.scheduleCreateService()會間接導致目標service走到大家熟悉的onCreate()。而後還會調用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;
. . . . . .
}
終於看到調用scheduleBindService()了。
到了“Service所屬的進程”裏,scheduleBindService()執行的代碼如下:
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token; // 對應AMS裏的ServiceRecord
s.intent = intent;
s.rebind = rebind;
......
sendMessage(H.BIND_SERVICE, s);
}
所發出的BIND_SERVICE消息,會導致service寄身的進程走到handleBindService():
【frameworks/base/core/java/android/app/ActivityThread.java】
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
. . . . . .
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
. . . . . .
}
回調了目標service的onBind(),而後向AMS發佈自己,即調用publishService()。
publishService()的第一個參數指代AMS裏的ServiceRecord節點,而最後一個參數是目標Service的onBind()函數返回的服務對象。因此publish函數其實起的是銜接的作用。
在AMS一側,publish動作最終會走到publishServiceLocked():
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
. . . . . .
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service; // 記錄下目標進程對應的binder代理
b.requested = true;
b.received = true;
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
. . . . . .
c.conn.connected(r.name, service); // 通告bindService發起方
. . . . . .
}
}
}
. . . . . .
}
這裏又牽扯到ServiceRecord的connections映射表,在介紹bindServiceLocked()函數時,我們其實已經看到過幾句相關的代碼了,現在再列舉一下:
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
. . . . . .
clist.add(c);
也就是說,我們在綁定服務時創建的那個ConnectionRecord節點,會同時將該節點記錄進ServiceRecord的connections映射表,而映射表的key值就是邏輯上指向發起端的IServiceConnection代理。
爲什麼要有這個映射表呢?很簡單,就是爲了快速地找出所有使用相同IServiceConnection.Stub對象,完成綁定動作的ConnectionRecord節點。請大家設想,我們完全可以在一個Activity裏,使用同一個ServiceConnection對象發起多次綁定同一個service的動作,發起綁定時使用的intent也許會不同,但最終有可能綁定到同一個service,此時,上面代碼中s.connections.get(binder)得到的ArrayList就會含有多個元素啦。
好,介紹完connections映射表,我們繼續看publishServiceLocked()裏的那句c.conn.connected()。ConnectionRecord節點的conn成員也是IServiceConnection類型的代理端,所以這一句調用最終是遠程調用到發起端的ServiceDispatcher裏的mIServiceConnection對象,該對象是InnerConnection類型的。
InnerConnection的connected()函數如下:
【frameworks/base/core/java/android/app/LoadedApk.java】
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
. . . . . .
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}
而ServiceDispatcher的connected()函數如下:
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()回調啦!而傳來的service參數,就是我們希望綁定的那個service提供的binder代理。現在我們可以說已經打通了bindService()動作涉及的三方關係:發起方、AMS、目標Service。我們不妨再畫一張圖看看:
有關Service的基本機制和啓動流程,我們就先說這麼多吧。以後我們再補充其他方面的內容。