之前的博客已經(jīng)介紹了應(yīng)用框架中的Activity和Application,今天來(lái)講四大組件之一的Service。對(duì)于Service大家肯定都比較熟悉,與Activity最大的不同就是Service不會(huì)與界面打交道,而是始終工作在后臺(tái),執(zhí)行一些與UI無(wú)關(guān)的操作和計(jì)算。即便用戶切換了其他應(yīng)用,啟動(dòng)的Service仍可在后臺(tái)運(yùn)行。一個(gè)組件可以與Service綁定并與之交互,甚至是跨進(jìn)程通信(IPC)。
Service運(yùn)行在主線程中(A service runs in the main thread of its hosting process),Service并不是一個(gè)新的線程,也不是新的進(jìn)程。也就是說(shuō),若您需要在Service中執(zhí)行較為耗時(shí)的操作(如播放音樂(lè)、執(zhí)行網(wǎng)絡(luò)請(qǐng)求等),需要在Service中創(chuàng)建一個(gè)新的線程。這可以防止ANR的發(fā)生,同時(shí)主線程可以執(zhí)行正常的UI操作。
Service有兩種啟動(dòng)方式,一個(gè)是startService,一個(gè)是bindService,接下來(lái)分別介紹一下兩種方式的啟動(dòng)邏輯。
1.startService
通常情況下啟動(dòng)一個(gè)Service的代碼如下:
Intent intent = new Intent(this, MyService.class);
context.startService(intent);
啟動(dòng)過(guò)程是從Context開(kāi)始的,而這個(gè)Context實(shí)際是一個(gè)ContextWrapper,而從ContextWrapper的實(shí)現(xiàn)看來(lái),其內(nèi)部實(shí)現(xiàn)都是通過(guò)ContextImpl來(lái)完成的,這是一種典型的橋接模式。通過(guò)調(diào)用ContextImpl的startService,會(huì)啟動(dòng)一個(gè)服務(wù),核心代碼如下所示:
private Component startServiceCommon(Intent service, UserHandle user) {
......
Component cn = ActivityManagerNative.getDefault().startService(mMainThread.getApplicationThread(),service,service.resolveTypeIfNeeded(getContentResolver()),user.getIdentifier());
......
ContextImpl通過(guò)ActivityManagerNative.getDefault()獲取到一個(gè)服務(wù),這個(gè)服務(wù)就是熟悉的Activity Manager Service(AMS),啟動(dòng)這個(gè)服務(wù)的方式當(dāng)然還是Binder機(jī)制。所起啟動(dòng)Service的工作就轉(zhuǎn)移到了AMS身上。在AMS的內(nèi)部還有一個(gè)mServices,這個(gè)對(duì)象是輔助AMS進(jìn)行service管理的類,包括Service的啟動(dòng)、綁定和停止等等。同時(shí)一個(gè)Service在AMS內(nèi)部對(duì)應(yīng)一個(gè)ServiceRecord,AMS用它來(lái)記錄各個(gè)Service。
而在AMS內(nèi)部會(huì)通過(guò)realStartServiceLocked方法來(lái)啟動(dòng)Service,其實(shí)在AMS內(nèi)部的啟動(dòng)步驟還有還經(jīng)過(guò)了很多方法,不過(guò)最為核心的就是realStartServiceLocked,該方法的核心代碼如下:
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
...... app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo, mMm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState);
r.postNotification();
......
}
這里的app是一個(gè)ProcessRecord對(duì)象,就是在之前的博客中提到的AMS中用于記錄一個(gè)Application的對(duì)象。通過(guò)app.thread.scheduleCreateService方法來(lái)創(chuàng)建Service并調(diào)用其onCreate方法,接著在通過(guò)sendServiceArgsLocked方法來(lái)調(diào)用Service的其他方法,比如onStartCommand。而這兩個(gè)過(guò)程均是進(jìn)程間通信,app.thread其實(shí)是一個(gè)IApplicationThread類型,實(shí)際就是一個(gè)Binder。而scheduleCreateService就是這個(gè)binder中的一個(gè)接口方法,接下來(lái)看一下對(duì)應(yīng)的scheduleCreateService方法:
public final void scheduleCreateService(Binder token, ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(procesState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
從代碼中可以看到,最后的創(chuàng)建工作又通過(guò)發(fā)送消息給Handler H將創(chuàng)建Service的工作又回到了ActivityThread中。最后再來(lái)看看ActivityThread的handleCreateService
private void handleCreateService(CreateServiceData data){
......
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);
service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
......
}
這個(gè)方法主要做了以下幾件事:
1.通過(guò)創(chuàng)建類加載器,創(chuàng)建Service實(shí)例
2.創(chuàng)建Application對(duì)象,并調(diào)用其onCreate方法,當(dāng)然Application對(duì)象只會(huì)被創(chuàng)建一次
3.創(chuàng)建ContextImpl對(duì)象,并通過(guò)service的onAttach方法建立兩者之間的聯(lián)系。這個(gè)過(guò)程和Activity類似,畢竟Activity和Service都是一個(gè)Context
4.最后調(diào)用Service的onCreate方法,并將Service保存在ActivityThread中的一個(gè)列表mServices。
由于Service的onCreate方法被執(zhí)行了,接下來(lái)AcitivtyThread還會(huì)通過(guò)handleServiceArgs方法調(diào)用Service的onStartCommand方法:
private void handleServiceArgs(ServiceData data) {
Service s = mServices.get(data.token);
......
if(!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
......
ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 1, data.startId, res);
......
}
在這個(gè)方法中可以看到,在service執(zhí)行完成之后,還會(huì)通過(guò)ActivityManagerNative.getDefault().serviceDoneExecuting來(lái)通知AMS service已經(jīng)執(zhí)行完畢。
最后來(lái)總結(jié)一下Service啟動(dòng)的主要步驟:
Context-->AMS-->app.thread-->ActivityThread-->Service
為什么要去繞這么一大圈呢?其實(shí)很好理解,AMS管理各個(gè)組件,要?jiǎng)?chuàng)建一個(gè)新的service當(dāng)然要通過(guò)AMS來(lái)維護(hù)一個(gè)與該service對(duì)應(yīng)的實(shí)例并與對(duì)應(yīng)的進(jìn)程實(shí)現(xiàn)關(guān)聯(lián),app.thread只是一個(gè)應(yīng)用通信的接口,并將對(duì)應(yīng)的工作交接給ActivityThread,ActivityThread才是應(yīng)用的真正實(shí)例,它當(dāng)然也要管理該Service,并維護(hù)一個(gè)對(duì)應(yīng)的記錄(mServices)。其實(shí)Activity和Service的啟動(dòng)過(guò)程大致相同,從中可以更加了解Android的應(yīng)用框架。
2.bindService
bindService的大致過(guò)程過(guò)程和startService類似,還是通過(guò)contextImpl.bindServiceCommon來(lái)啟動(dòng):
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, UserHandle user) {
IServiceConnction sd;
......
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), mMainThread.getHandler(), flags);
......
int res = ActivityManagerNative.getDefault().bindService(mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, user.getIdentifier());
......
}
這里主要做了兩件事:
將客戶端的ServiceConnection轉(zhuǎn)化為ServiceDispatcher.InnerConnection。之所以不能直接使用ServiceConnection是因?yàn)榻壎ǖ姆?wù)可能是跨進(jìn)程的,所以必須借助于Binder才能讓遠(yuǎn)程服務(wù)回調(diào)自己的方法。而ServiceDispatcher的內(nèi)部類InnerConnction正好充當(dāng)了這個(gè)Binder。所以ServiceDispatcher的作用就是ServiceConnection和InnerConnection連接的橋梁。
調(diào)用AMS的bindService方法來(lái)完成Service的具體綁定過(guò)程。
接下來(lái)重點(diǎn)講一下AMS的bindService方法。和startService方法類似的是,bindService最終會(huì)將調(diào)用到app.thread.scheduleBindService():
public final void scheduleBindService(Binder token, Intent intent, boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
......
sendMessage(H.BIND_SERVICE, s);
}
接下來(lái)又轉(zhuǎn)移到了ActivityThread中,而這個(gè)方法就是ActivityThread.handleBindService():
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
......
IBiner binder = s.onBind(data.intent); ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
......
}
在handleBindService中,首先根據(jù)Service的token取出Service對(duì)象,然后調(diào)用Service的onBind方法。但是onBind方法是Service的方法,這個(gè)時(shí)候客戶端并不知道已經(jīng)綁定成功了,所以還必須調(diào)用客戶端的ServiceConnection中的onServiceConnected,這個(gè)是由ActivityManagerNative.getDefault().publishService方法來(lái)完成的。最終指令流會(huì)轉(zhuǎn)移到mServices(AMS內(nèi)部的輔助Service)的publishServiceLocked。其核心代碼只有一行:c.conn.connected(r.name, service),其中c.conn類型是ServiceDispatcher.InnerConnection,service就是Service的onBind返回的Binder對(duì)象。接下來(lái)看看ServiceDispatcher.InnerConnection的定義:
private static class InnerConnection extends IServiceConnection.Stub {
...
private void connected(ComponentName name, Binder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispacher.get();
if(sd != null) {
sd.connectd(name, service);
}
}
}
InnerConnection最后通過(guò)ServiceDispatcher的connected方法來(lái)調(diào)用ServiceConnection的onServiceConnected,至此綁定完成。