文中的原始碼版本為api23
Service銷燬流程
stopService流程
流程簡圖如下
關閉Service我們通常使用Context.stopService
,該方法會經歷以下方法呼叫
Context.stopService
->
ContextImpl.stopService
->
ContextImpl.stopServiceCommon
->
ActivityManagerService.stopServcie
->
ActiveServcie.stopServiceLocked
ActiveServcie.stopServiceLocked
int stopServiceLocked(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
//...
// If this service is active, make sure it is stopped.
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, null,
Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false);
if (r != null) {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
try {
stopServiceLocked(r.record);
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
return -1;
}
return 0;
}
private void stopServiceLocked(ServiceRecord service) {
//...
//設定startRequested為false,在後面的流程中有用
service.startRequested = false;
//...
service.callStart = false;
bringDownServiceIfNeededLocked(service, false, false);
}
複製程式碼
該方法邏輯比較簡單,通過retrieveServiceLocked
方法找到服務記錄,然後呼叫過載方法stopServiceLocked
繼續下面的流程,該方法內部直接呼叫了bringDownServiceIfNeededLocked
ActiveServices.bringDownServiceIfNeededLocked
private final void bringDownServiceIfNeededLocked(ServiceRecord r,
boolean knowConn/*false*/,
boolean hasConn/*false*/) {
//...
//判斷是否有bind
if (isServiceNeeded(r, knowConn, hasConn)) {
return;
}
// Are we in the process of launching?
if (mPendingServices.contains(r)) {
return;
}
bringDownServiceLocked(r);
}
複製程式碼
該方法做了三件事情
- 呼叫
isServiceNeeded
方法判斷服務是否還有必要存在,如果有則直接返回 - 檢視服務是否存在於待啟動服務列表中,如果是則直接返回
- 上面的條件都不滿足的話則會呼叫
bringDownServiceLocked
繼續停止服務的流程
ActiveServices.isServiceNeeded
private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn, boolean hasConn) {
// 該欄位在stopServiceLocked方法中已經被置為false
if (r.startRequested) {
return true;
}
// knowConn此時為false,所以會走這個if流程
if (!knowConn) {
hasConn = r.hasAutoCreateConnections();
}
if (hasConn) {
return true;
}
return false;
}
//ServiceRecord.java
public boolean hasAutoCreateConnections() {
// XXX should probably keep a count of the number of auto-create
// connections directly in the service.
for (int conni=connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i=0; i<cr.size(); i++) {
if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
return true;
}
}
}
return false;
}
複製程式碼
isServiceNeeded
內部又呼叫了hasAutoCreateConnections
hasAutoCreateConnections
會檢測當前服務的繫結記錄(bindService
記錄),在這些記錄中只要有使用了帶有BIND_AUTO_CREATE
標誌的Intent
則返回true,表示不允許關閉服務。從這點可以看出,呼叫stopService
之後並不一定會真正的關閉服務。
假設服務未被繫結,我們繼續下面的流程
ActiveServices.bringDownServiceLocked
private final void bringDownServiceLocked(ServiceRecord r) {
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
for (int i=0; i<c.size(); i++) {
ConnectionRecord cr = c.get(i);
cr.serviceDead = true;
try {
//斷開客戶端連線
//通過IPC觸發ServiceConnectino.onServiceDisconnected
cr.conn.connected(r.name, null);
} catch (Exception e) {
//...
}
}
}
if (r.app != null && r.app.thread != null) {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
//...
if (ibr.hasBound) {
try {
//...
ibr.hasBound = false;
//通過IPC觸發Service.onUnbind方法
r.app.thread.scheduleUnbindService(r,
ibr.intent.getIntent());
} catch (Exception e) {
//...
}
}
}
}
//...
//清理服務記錄
final ServiceMap smap = getServiceMap(r.userId);
smap.mServicesByName.remove(r.name);
smap.mServicesByIntent.remove(r.intent);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r, 0, true);
//從待啟動流程中移除
for (int i=mPendingServices.size()-1; i>=0; i--) {
if (mPendingServices.get(i) == r) {
mPendingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed pending: " + r);
}
}
//...
//一些清理工作
r.clearDeliveredStartsLocked();
r.pendingStarts.clear();
if (r.app != null) {
//...
r.app.services.remove(r);
if (r.app.thread != null) {
//...
try {
//...
mDestroyingServices.add(r);
r.destroying = true;
mAm.updateOomAdjLocked(r.app);
//通過IPC觸發Service.onDestory
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
//...
}
} else {
//...
}
} else {
//...
}
//...
}
複製程式碼
停止服務的邏輯還是挺清晰的
- 關閉所有的客戶端連線,這個階段就是通過IPC觸發客戶端的
ServiceConnection.onServiceDisconnected
- 通過IPC觸發服務的
onUnbind
生命週期方法 - 清理一些資源
- 通過IPC觸發服務的
onDestory
生命週期方法
此時服務就真正走向了生命的終點了。
unbindService流程
流程簡圖如下
Context.unbindService
方法會經歷以下呼叫鏈
Context.unbindService
->
ContextImpl.unbindService
->
ActivityManagerService.unbindService
->
ActiveService.unbindServiceLocked
ActiveService.unbindServiceLocked
boolean unbindServiceLocked(IServiceConnection connection) {
//對應於客戶端的ServiceConnection
IBinder binder = connection.asBinder();
//可以使用同一個ServiceConnection連線多個Service
//因此這裡拿出來是一個List
ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
//...
final long origId = Binder.clearCallingIdentity();
try {
while (clist.size() > 0) {
ConnectionRecord r = clist.get(0);
removeConnectionLocked(r, null, null);
//...
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return true;
}
複製程式碼
客戶端的一個ServiceConnection
例項可以bind
至多個Service
,對應於AMS
這邊就會儲存多個ConnectionRecord
。
unbindServiceLocked
內使用了一個while
迴圈,依次對每個ConnectionRecord
呼叫removeConnectionLocked
。
ActiveServices.removeConnectionLocked
void removeConnectionLocked(
ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
//移除ServiceRecord以及客戶端ProcessRecord等內部維護的
//ConnectionRecord
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
s.connections.remove(binder);
}
}
b.connections.remove(c);
if (c.activity != null && c.activity != skipAct) {
if (c.activity.connections != null) {
c.activity.connections.remove(c);
}
}
if (b.client != skipApp) {
b.client.connections.remove(c);
//...
}
clist = mServiceConnections.get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
mServiceConnections.remove(binder);
}
}
//...
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
}
//此處serviceDead為false
if (!c.serviceDead) {
//...
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
//...
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
b.intent.doRebind = false;
s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
serviceProcessGoneLocked(s);
}
}
//這個flags就是呼叫bindService時用的flags
if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
boolean hasAutoCreate = s.hasAutoCreateConnections();
//...
bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
}
}
}
複製程式碼
removeConnectionLocked
首先會做一些清理工作,之後會呼叫ApplicationThread.scheduleUnbindService
方法觸發Service.onUnbind
其次,如果發起bind
請求時所用的flags
中包含BIND_AUTO_CREATE
標誌,還會觸發bringDownServiceIfNeededLocked
hasAutoCreateConnections
和bringDownServiceIfNeededLocked
方法我們在分析stopService
流程的時候已經分析過了,就不展開講了,這裡只討論bringDownServiceIfNeededLocked
入參變化而引起的一些變化。
假設當前沒有其他客戶端繫結至該服務,那麼此時hasAutoCreate
應該為false
,那麼bringDownServiceIfNeededLocked
的形參,knowConn
為true
,hasConn
為false
這兩個引數,只會在bringDownServiceIfNeededLocked
內呼叫isServiceNeeded
方法使用到,再貼一下這個方法的程式碼
private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn/*true*/, boolean hasConn/*false*/) {
// startRequested只有在呼叫過startService才會被置為true
// 這裡為false
if (r.startRequested) {
return true;
}
// Is someone still bound to us keepign us running?
if (!knowConn) {
hasConn = r.hasAutoCreateConnections();
}
if (hasConn) {
return true;
}
return false;
}
複製程式碼
可以看到isServiceNeeded
此時返回false
,因此銷燬的流程還會繼續進行下去。
後續的流程就跟stopService
的一樣了。