文中的源代碼版本爲api23java
啓動Service的方式有兩種:startService
、bindService
api
先祭上一張流程圖看個大概app
Service
進程已經啓動
Context.startService
會經歷如下調用鏈 Context.startService
-> ContextImpl.startService
-> ContextImpl.startServiceCommon
post
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
//作一些校驗,如Android L版本及以上不容許隱式Intent啓動Service
validateServiceIntent(service);
service.prepareToLeaveProcess();
//調用AMS的startService
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
if (cn != null) {
//一些異常判斷
}
return cn;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
複製代碼
該方法最核心的做用是將啓動Service
的請求經過IPC
發送給了AMS
ui
AMS
接收到請求以後會經歷如下方法調用 ActivityManagerService.startService
-> ActiveService.startServiceLocked
spa
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,//mimetype int callingPid, int callingUid, String callingPackage, int userId) throws TransactionTooLargeException {
//...
//記錄啓動Service的進程是否處於前臺
final boolean callerFg;
if (caller != null) {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
//...
}
callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
} else {
callerFg = true;
}
//根據Intent信息查找ServiceLookupResult
//ServiceLookupResult由ServiceRecord以及Service對應的permisson組成
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg);
//...
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
//保存請求數據,服務啓動以後會遍歷pendingStarts,取出StartItem
//拿裏面的數據觸發Service.onStartCommand
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
//ServiceMap與userId一一對應,保存服務信息
final ServiceMap smap = getServiceMap(r.userId);
//是否添加到服務啓動列表中
//只有是後臺進程發起調用,且目標進程未啓動或者state>=PROCESS_STATE_SERVICE
//才爲true
boolean addToStarting = false;
//延遲啓動的判斷
if (!callerFg//調用方處於後臺,可能會延遲啓動Service
&& r.app == null //ServiceRecord未被分配進程
&& mAm.mStartedUsers.get(r.userId) != null) {
//獲取應用進程實例
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
//...
if (r.delayed) {
// This service is already scheduled for a delayed start; just leave
// it still waiting.
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
return r.name;
}
if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
// Something else is starting, delay!
Slog.i(TAG_SERVICE, "Delaying start of: " + r);
smap.mDelayedStartList.add(r);
r.delayed = true;
return r.name;
}
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
addToStarting = true;
} else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
addToStarting = true;
//...
} else if (DEBUG_DELAYED_STARTS) {
//debug信息
}
} else if (DEBUG_DELAYED_STARTS) {
//debug信息
}
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
複製代碼
ServiceMap
這個結構用於保存用戶當前運行的服務信息,retrieveServiceLocked
方法邏輯就是經過Intent
從ServiceMap
中查找Service
,若是當前服務還未啓動,那麼會建立一個ServiceRecord
對象,保存在ServiceMap
中。 還有一個須要注意的點是後臺服務以及延遲啓動機制,全部的後臺服務都會被添加至一個名爲mStartingBackground
的列表中(添加邏輯在startServiceInnerLocked
方法中),若是當先後臺服務超過閾值,那麼服務將會被添加至延遲啓動列表中。debug
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg,//服務啓動者是不是前臺進程 boolean addToStarting) //是否添加到後臺服務列表中 throws TransactionTooLargeException {
//...
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
if (error != null) {
return new ComponentName("!!", error);
}
//將服務添加至後臺服務列表中
if (r.startRequested && addToStarting) {
boolean first = smap.mStartingBackground.size() == 0;
smap.mStartingBackground.add(r);
r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT;
//...
if (first) {
smap.rescheduleDelayedStarts();
}
} else if (callerFg) {
smap.ensureNotStartingBackground(r);
}
return r.name;
}
複製代碼
該方法主要是調用了bringUpServiceLocked
來進行後續的服務啓動功能,同時會判斷addToStarting
值決定是否將服務添加至後臺服務列表中。rest
private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting) throws TransactionTooLargeException {
//服務進程已經啓動,且服務也已啓動,分發onStartCommand事件
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
//...
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
ProcessRecord app;
//應用進程已經啓動,調用realStartServiceLocked,返回
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else {
app = r.isolatedProc;
}
//啓動服務進程
if (app == null) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
//進程啓動須要時間,先將服務保存起來
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
//...
return null;
}
複製代碼
這個方法根據服務進程的狀態以及服務的狀態產生了幾個分支code
sendServiceArgsLocked
方法觸發Service.onStartCommand
isotate
模式啓動,那麼調用ActivityManagerService.getProcessRecordLocked
查找是否有可用進程,若是有就調用realStartServiceLocked
啓動服務ActivityManagerService.startProcessLocked
啓動進程,同時將服務保存至待啓動列表中,等待進程啓動完畢以後繼續下面的流程。sendServiceArgsLocked
這個方法在realStartServiceLocked
中也會被調用,咱們到時候再分析。cdn
關於進程啓動,你們能夠參考其餘文章,這裏不去展開講。 進程啓動以後,最終仍是會進入到realStartServiceLocked
方法中,方法調用鏈以下 ActivityThread.main
-> ActivityThread.attach
-> ActivityManagerService.attachApplication
-> ActivityManagerService.attachApplicationLocked
-> ActiveService.attachApplicationLocked
-> ActiveService.realStartServiceLocked
下面咱們就來分析一下ActiveService.realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
//...
boolean created = false;
try {
//...
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
//...
} finally {
//...
}
//step2
requestServiceBindingsLocked(r, execInFg);
//...
//step3
sendServiceArgsLocked(r, execInFg, true);
//...
}
複製代碼
這個方法我作了不少精簡,咱們只關注一些最核心的部分
ApplicationThrad.scheduleCreateService
方法,最終會走到ActivityThread.handleCreateService
中,該方法經過反射建立一個Service
的實例,並調用其onCreate
方法,這部分的代碼並不複雜,你們能夠自行分析。requestServiceBindingsLocked
處理bindService
請求,咱們放到bindService
啓動流程去講sendServiceArgsLocked
處理startService
請求下面咱們分析一下sendServiceArgsLocked
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, boolean oomAdjusted) throws TransactionTooLargeException {
final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
while (r.pendingStarts.size() > 0) {
Exception caughtException = null;
ServiceRecord.StartItem si;
try {
si = r.pendingStarts.remove(0);
//...
si.deliveredTime = SystemClock.uptimeMillis();
r.deliveredStarts.add(si);
si.deliveryCount++;
//...
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (TransactionTooLargeException e) {
//...
} catch (RemoteException e) {
//...
} catch (Exception e) {
//...
}
//...
}
}
複製代碼
還記得pendingStarts
麼?在ActiveService.startServiceLocked
方法中,ActiveService
將啓動服務的請求的信息保存在pendingStarts
中。 sendServiceArgsLocked
的邏輯很是簡單,就是遍歷pendingStarts
,將全部的start
請求發送出去。 發送的過程有涉及到了進程間通訊,調用了ApplicationThrad.scheduleServiceArgs
方法,該方法最終會進入到ActivityThread.handleServiceArgs
中,觸發Service.onStartCommand
。
至此,startService
的流程就結束了。
bindService
的大體流程以下
Context.bindService
會經歷如下調用鏈 Context.bindService
-> ContextImpl.bindService
-> ContextImpl.bindServiceCommon
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, UserHandle user) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
//bindService成功以後會回調給ServiceConnection一個binder對象
//用於後續的C/S通訊,AMS直接經過IServiceConnection接口進行回調
//因爲ServiceConnection不是aidl接口,所以須要進行必定的包裝
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
//...
service.prepareToLeaveProcess();
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
if (res < 0) {
//...
}
return res != 0;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
複製代碼
bindServiceCommon
的主要職責
LoadedApk.getServiceDispatcher
是包裝ServiceConnection
成一個IServiceConnection
對象,後續AMS
能夠直接經過該對象發佈客戶端與服務端進行通訊的Binder
對象。這個方法就不展開講了。ActivityManagerService.bindService
方法繼續服務啓動流程ActivityManagerService.bindService
會直接調用ActiveService.bindServiceLocked
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
//bind請求發起進程
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
//...
//請求發起進程是否處於前臺
final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
//...
ServiceRecord s = res.record;
final long origId = Binder.clearCallingIdentity();
try {
//...
//下面這些操做是在AMS中創建起客戶端和服務端的綁定關係
//主要是保存客戶端的一些信息到ServiceRecord中
//保存服務端的一些信息到客戶端(ActivityRecord、ProcessRecord)中
//retrieveAppBindingLocked方法執行完畢以後會在ServiceRecord的bindings
//字段中添加一條記錄。bindings是Map類型,值類型爲IntentBindRecord
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
activity.connections = new HashSet<ConnectionRecord>();
}
activity.connections.add(c);
}
b.client.connections.add(c);
//...
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
//...
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
//conn就是客戶端建立的IServiceConnection對象
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
//...
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
//...
} finally {
//...
}
return 1;
}
複製代碼
該方法的核心有兩點
bringUpServiceLocked
啓動服務IServiceConnection.connected
方法發佈用於客戶端與服務端進行通訊的binder
對象看似簡單,可是若是將BIND_AUTO_CREATE
標誌以及bringUpServiceLocked
方法內部邏輯分支,場景會變得稍微複雜一些。這裏我對各個場景下Service.onBind
和ServiceConnection.onServiceConnected
的調用時機作一下簡單分析
BIND_AUTO_CREATE
標誌,服務未啓動(包括進程未啓動)因爲設置了BIND_AUTO_CREATE
標誌,接下來就會進入到bringUpServiceLocked
中。該方法在分析startService
流程的時候咱們分析過。若是進程已經啓動則會進入到realStartServiceLocked
中,若是進程未啓動,那麼待進程啓動以後經過進程間通訊最終仍是會走到realStartServiceLocked
方法中。 realStartServiceLocked
方法內會經過進程間通訊,通知服務進程建立Service
實例,並調用onCreate方法
,以後會調用requestServiceBindingsLocked
方法處理bind
請求,這個方法以前特地留到這裏進行分析。
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) throws TransactionTooLargeException {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
break;
}
}
}
複製代碼
該方法很簡單,遍歷ServiceRecord.bindings
的值,並挨個兒調用requestServiceBindingLocked
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {
//...
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
//...
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
//...
} catch (TransactionTooLargeException e) {
//...
} catch (RemoteException e) {
//...
}
}
return true;
}
複製代碼
requestServiceBindingLocked
也很簡單,經過Binder
通訊,調用ApplicationThread.scheduleBindService
方法,該方法在通過一些列調用以後,Service.onBind
就會被調用。
ServiceConnection.onServiceConnected
的回調則是在Service.onBind
方法被調用以後。 ActivityThread
在調完Service.onBind
以後,會回調AMS
,最終進入到ActiveService.publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
//...
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
//...
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);
//...
try {
c.conn.connected(r.name, service);
} catch (Exception e) {
//...
}
}
}
}
//...
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
複製代碼
publishServiceLocked
調用了IServiceConnection.connected
,以後會通過進程間通訊以及IServiceConnection
實現類的內部邏輯,最終調用ServiceConnection.onServiceConnected
方法。
此時bringUpServiceLocked
會立刻返回,繼續bindServiceLocked
的流程
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
//...
try {
//...
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
//conn就是客戶端建立的IServiceConnection對象
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
//...
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
//...
} finally {
//...
}
return 1;
}
複製代碼
這裏要注意的有兩個字段IntentBindRecord.requested
和IntentBindRecord.received
,它們會影響接下來的流程走向。
Application.scheduleBindService
調用,通知服務進程回調Service.onBind
方法。true
,說明Service.onBind
方法已經被回調而此時因爲尚未調用ApplicationThread.scheduleBindService
,所以received
爲false
,requested
也是false
,這時走requestServiceBindingLocked
方法。 後續的流程,則跟case 1
是同樣的。
若是未設置BIND_AUTO_CREATE,那麼在bind
流程中是不會調用bringUpServiceLocked
啓動服務的。而此時服務已經啓動,那麼多是以前有調用過startService
啓動過服務。 此時received
爲false
,requested
也是false
,直接走requestServiceBindingLocked
,跟case 2
是同樣的
這種場景就須要等待服務啓動了,服務啓動的機制是下一次的startCommand
被調用或者帶有BIND_AUTO_CREATE
標誌的bindService
被調用。不管進程是否啓動,最終都會走到realStartServiceLocked
,後續流程case 1
同樣。
至此,bindService
的流程就分析完畢了。