之前的博客已經介紹了應用框架中的Activity
和Application
,今天來講四大組件之一的Service
。對于Service
大家肯定都比較熟悉,與Activity
最大的不同就是Service
不會與界面打交道,而是始終工作在后臺,執行一些與UI無關的操作和計算。即便用戶切換了其他應用,啟動的Service仍可在后臺運行。一個組件可以與Service綁定并與之交互,甚至是跨進程通信(IPC)。
Service運行在主線程中(A service runs in the main thread of its hosting process),Service并不是一個新的線程,也不是新的進程。也就是說,若您需要在Service中執行較為耗時的操作(如播放音樂、執行網絡請求等),需要在Service中創建一個新的線程。這可以防止ANR的發生,同時主線程可以執行正常的UI操作。
Service
有兩種啟動方式,一個是startService
,一個是bindService
,接下來分別介紹一下兩種方式的啟動邏輯。
1.startService
通常情況下啟動一個Service
的代碼如下:
Intent intent = new Intent(this, MyService.class);
context.startService(intent);
啟動過程是從Context
開始的,而這個Context
實際是一個ContextWrapper
,而從ContextWrapper
的實現看來,其內部實現都是通過ContextImpl
來完成的,這是一種典型的橋接模式。通過調用ContextImpl
的startService
,會啟動一個服務,核心代碼如下所示:
private Component startServiceCommon(Intent service, UserHandle user) {
......
Component cn = ActivityManagerNative.getDefault().startService(mMainThread.getApplicationThread(),service,service.resolveTypeIfNeeded(getContentResolver()),user.getIdentifier());
......
ContextImpl
通過ActivityManagerNative.getDefault()
獲取到一個服務,這個服務就是熟悉的Activity Manager Service(AMS)
,啟動這個服務的方式當然還是Binder機制。所起啟動Service
的工作就轉移到了AMS身上。在AMS的內部還有一個mServices
,這個對象是輔助AMS進行service管理的類,包括Service的啟動、綁定和停止等等。同時一個Service在AMS內部對應一個ServiceRecord
,AMS用它來記錄各個Service。
而在AMS內部會通過realStartServiceLocked
方法來啟動Service,其實在AMS內部的啟動步驟還有還經過了很多方法,不過最為核心的就是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是一個ProcessRecord
對象,就是在之前的博客中提到的AMS中用于記錄一個Application的對象。通過app.thread.scheduleCreateService
方法來創建Service
并調用其onCreate
方法,接著在通過sendServiceArgsLocked
方法來調用Service
的其他方法,比如onStartCommand
。而這兩個過程均是進程間通信,app.thread
其實是一個IApplicationThread
類型,實際就是一個Binder
。而scheduleCreateService
就是這個binder中的一個接口方法,接下來看一下對應的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);
}
從代碼中可以看到,最后的創建工作又通過發送消息給Handler H將創建Service的工作又回到了ActivityThread中。最后再來看看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);
......
}
這個方法主要做了以下幾件事:
1.通過創建類加載器,創建Service實例
2.創建Application對象,并調用其onCreate方法,當然Application對象只會被創建一次
3.創建ContextImpl對象,并通過service的onAttach方法建立兩者之間的聯系。這個過程和Activity類似,畢竟Activity和Service都是一個Context
4.最后調用Service的onCreate方法,并將Service保存在ActivityThread中的一個列表mServices。
由于Service的onCreate
方法被執行了,接下來AcitivtyThread
還會通過handleServiceArgs
方法調用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);
......
}
在這個方法中可以看到,在service
執行完成之后,還會通過ActivityManagerNative.getDefault().serviceDoneExecuting
來通知AMS service已經執行完畢。
最后來總結一下Service啟動的主要步驟:
Context-->AMS-->app.thread-->ActivityThread-->Service
為什么要去繞這么一大圈呢?其實很好理解,AMS管理各個組件,要創建一個新的service當然要通過AMS來維護一個與該service對應的實例并與對應的進程實現關聯,app.thread只是一個應用通信的接口,并將對應的工作交接給ActivityThread,ActivityThread才是應用的真正實例,它當然也要管理該Service,并維護一個對應的記錄(mServices)。其實Activity和Service的啟動過程大致相同,從中可以更加了解Android的應用框架。
2.bindService
bindService
的大致過程過程和startService
類似,還是通過contextImpl.bindServiceCommon
來啟動:
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轉化為ServiceDispatcher.InnerConnection。之所以不能直接使用ServiceConnection是因為綁定的服務可能是跨進程的,所以必須借助于Binder才能讓遠程服務回調自己的方法。而ServiceDispatcher的內部類InnerConnction正好充當了這個Binder。所以ServiceDispatcher的作用就是ServiceConnection和InnerConnection連接的橋梁。
調用AMS的bindService方法來完成Service的具體綁定過程。
接下來重點講一下AMS的bindService方法。和startService
方法類似的是,bindService
最終會將調用到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);
}
接下來又轉移到了ActivityThread
中,而這個方法就是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
中,首先根據Service的token取出Service對象,然后調用Service的onBind方法。但是onBind方法是Service的方法,這個時候客戶端并不知道已經綁定成功了,所以還必須調用客戶端的ServiceConnection中的onServiceConnected,這個是由ActivityManagerNative.getDefault().publishService
方法來完成的。最終指令流會轉移到mServices(AMS內部的輔助Service)的publishServiceLocked。其核心代碼只有一行:c.conn.connected(r.name, service)
,其中c.conn類型是ServiceDispatcher.InnerConnection,service就是Service的onBind返回的Binder對象。接下來看看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最后通過ServiceDispatcher的connected方法來調用ServiceConnection的onServiceConnected,至此綁定完成。