文中的原始碼版本為api23
Service啟動流程
啟動Service的方式有兩種:startService
、bindService
startService
先祭上一張流程圖看個大概
此圖省略了一些程式間通訊的細節,同時假設Service
程式已經啟動
Context.startService
會經歷以下呼叫鏈
Context.startService
->
ContextImpl.startService
->
ContextImpl.startServiceCommon
ContextImpl.startServiceCommon
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
AMS
接收到請求之後會經歷以下方法呼叫
ActivityManagerService.startService
->
ActiveService.startServiceLocked
ActiveService.startServiceLocked
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
方法中),如果當前後臺服務超過閾值,那麼服務將會被新增至延遲啟動列表中。
ActiveService.startServiceInnerLocked
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
值決定是否將服務新增至後臺服務列表中。
ActiveService.bringUpServiceLocked
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;
}
複製程式碼
這個方法根據服務程式的狀態以及服務的狀態產生了幾個分支
- 如果服務程式已經啟動,且服務也已經啟動,那麼呼叫
sendServiceArgsLocked
方法觸發Service.onStartCommand
- 如果服務還未啟動,且不是以
isotate
模式啟動,那麼呼叫ActivityManagerService.getProcessRecordLocked
查詢是否有可用程式,如果有就呼叫realStartServiceLocked
啟動服務 - 服務未啟動,且服務的程式也沒啟動,那麼呼叫
ActivityManagerService.startProcessLocked
啟動程式,同時將服務儲存至待啟動列表中,等待程式啟動完畢之後繼續下面的流程。
sendServiceArgsLocked
這個方法在realStartServiceLocked
中也會被呼叫,我們到時候再分析。
關於程式啟動,大家可以參考其他文章,這裡不去展開講。
程式啟動之後,最終還是會進入到realStartServiceLocked
方法中,方法呼叫鏈如下
ActivityThread.main
->
ActivityThread.attach
->
ActivityManagerService.attachApplication
->
ActivityManagerService.attachApplicationLocked
->
ActiveService.attachApplicationLocked
->
ActiveService.realStartServiceLocked
下面我們就來分析一下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
ActiveService.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
bindService
的大致流程如下
Context.bindService
會經歷以下呼叫鏈
Context.bindService
->
ContextImpl.bindService
->
ContextImpl.bindServiceCommon
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
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
請求,這個方法之前特意留到這裡進行分析。
ActiveService.requestServiceBindingsLocked
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
ActiveService.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
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
方法。
case 2 : 設定BIND_AUTO_CREATE標誌,服務已啟動
此時bringUpServiceLocked
會馬上返回,繼續bindServiceLocked
的流程
ActiveService.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
,它們會影響接下來的流程走向。
- IntentBindRecord.requested
是否已經發起了
Application.scheduleBindService
呼叫,通知服務程式回撥Service.onBind
方法。 - IntentBindRecord.received
該欄位如果為
true
,說明Service.onBind
方法已經被回撥
而此時由於還沒有呼叫ApplicationThread.scheduleBindService
,因此received
為false
,requested
也是false
,這時走requestServiceBindingLocked
方法。
後續的流程,則跟case 1
是一樣的。
case 3 : 未設定BIND_AUTO_CREATE,服務已啟動
如果未設定BIND_AUTO_CREATE,那麼在bind
流程中是不會呼叫bringUpServiceLocked
啟動服務的。而此時服務已經啟動,那麼可能是之前有呼叫過startService
啟動過服務。
此時received
為false
,requested
也是false
,直接走requestServiceBindingLocked
,跟case 2
是一樣的
case 4 : 未設定BIND_AUTO_CREATE,服務未啟動
這種場景就需要等待服務啟動了,服務啟動的機制是下一次的startCommand
被呼叫或者帶有BIND_AUTO_CREATE
標誌的bindService
被呼叫。無論程式是否啟動,最終都會走到realStartServiceLocked
,後續流程case 1
一樣。
至此,bindService
的流程就分析完畢了。