本文旨在記錄一下自己學習 Android Service 源碼時的筆記。
startService
首先看一下大致的流程圖
上圖中,AMS代表ActivityManagerService, AS代表ActiveServices。 這里假設Service的啟動者(ProcessA)和 Service(ProcessB)不在同一個進程, 當然 startService 方式更多情況下還是用在自己進程內部。 這里只列出一些我認為比較主要的函數,忽略了異常情況下的處理, 例如延遲start, 找不到對應進程等。 圖中顏色加深的幾個關鍵方法:
1) AS.retrieveServiceLocked: 從PackageManager查詢要啟動的Service信息
2)AMS.checkAllowBackgroundLocked: 檢查是否允許從后臺啟動Service
3)AS.bringUpServiceLocked: 對Service和Process的情況進行判度并處理,下面的那幾個判斷流程都是在該函數里
4)AMS.startProcessLocked: 啟動Service對應的進程
5)AS.realStartServiceLocked: 使用IApplicationThread接口向Service進程發送create消息
6)AS.sendServiceArgsLocked: 使用IApplicationThread接口向Service進程發送start消息
7)Service.onStartCommand & Service.onCreate: Service callback
下面開始代碼分析。
Context.startService
--> ContextImpl.startService
--> ContextImpl.startServiceCommon
前面經過Context和它的Wrapper模式下的調用棧, 進入ContextImpl.startServiceCommon
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
//判斷Intent中是否符合要求, Android L以上版本需要顯示調用Service
validateServiceIntent(service);
//跨進程安全檢查
service.prepareToLeaveProcess(this);
//調用AMS接口
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
......
}
return cn;
} catch (RemoteException e) {
......
}
}
startServiceCommon會先做一些檢查,其中 Intent.prepareToLeaveProcess對跨進程做安全性檢查,例如在N以后的版本,在StrictMode下,不能使用scheme為"file://"的URI進行數據傳輸,必須使用FileProvider代替
public void prepareToLeaveProcess(boolean leavingPackage) {
......
switch (mAction) {
......
default:
// N以上版本對scheme為"file://" 的URI需要用FileProvider代替
mData.checkFileUriExposed("Intent.getData()");
}
}
然后調用服務端 AMS.startService,在此我們省去 Binder 調用流程,直接進入ActivityManagerService
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
......
synchronized(this) {
......
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
......
return res;
}
}
AMS.startService先對參數檢查, 然后調用ActiveServices.startServiceLocked
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, final int userId)
throws TransactionTooLargeException {
......
// 1. 查詢待啟動Service的信息
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
......
// 2. 檢查是否可以在后臺啟動
if (!r.startRequested) {
try {
final int allowed = mAm.checkAllowBackgroundLocked(
r.appInfo.uid, r.packageName, callingPid, true);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
......
return null;
} finally {
......
}
}
}
......
r.startRequested = true;
......
//startService可以多次向Service傳遞信息,每次的信息都是一個StartItem,對應著一個StartId
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
// 3. 下面的部分用于判斷是否需要延遲啟動Service
final ServiceMap smap = getServiceMap(r.userId);
// addToStarting值決定是否將待啟動的Service加入后臺啟動隊列ServiceMap.mStartingBackground
// 這里只是判斷, 它將被傳入最后的startServiceInnerLocked, 在那里使用
// 另外一個隊列ServiceMap.mDelayedStartList則是延遲啟動隊列
boolean addToStarting = false;
if (!callerFg && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) {
//通過AMS查詢Service對應的進程信息
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
//若Service對應的進程未啟動,或優先級過低,則需要將Service加入后臺啟動隊列
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
......
//若當前用戶啟動的后臺服務數量已超上限,則延遲啟動服務
if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
smap.mDelayedStartList.add(r);
r.delayed = true;
return r.name;
}
addToStarting = true;
} else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
......
addToStarting = true;
}
......
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
startServiceLocked 做了一些前期準備:
1. 查詢待啟動Service的信息
2. 檢查是否可以在后臺啟動
3. 檢查是否需要延遲啟動Service
這里第1步涉及到AMS對已啟動Service信息的存儲數據結構,我們在后面 AMS對Service的存儲 里一起分析.
第2步, O 上有一些改動, 具體可以看后面 Background Service in Android O 部分.
第3步將判斷待啟動的Service是否需要在 Background start, 或者是 Delay start, 總體規則就是如果進程未啟動或者優先級低,則 Background start, 如果連 Background start 隊列都已滿,則加入 Delay 隊列。 這一部分的看的不是很明白,特別是兩個隊列的關系, 我本以為是兩個優先級的隊列,但是看后面的處理函數ServiceMap.rescheduleDelayedStarts,好像不是, 以后有機會再看看。
檢查完成之后進入startServiceInnerLocked
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
......
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
......
// 這里會根據前面傳進來的addToStarting值處理延遲Service
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 里
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
......
// Case 1. 如果Service已經啟動過,直接發送start
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
......
// Case 2. Service沒啟動過
// 真正啟動之前再做一些檢查并處理, 例如service是否還有delay標志,service所處的進程時否結束了
......
final String procName = r.processName;
ProcessRecord app;
if (!isolated) {
// 查詢Service對應的進程信息
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (app != null && app.thread != null) {
try {
......
// Case 2.1 查詢到進程, 則直接start Service
realStartServiceLocked(r, app, execInFg);
return null;
}
......
}
} else {
......
}
// Case 2.2 查詢不到進程,則需要先start Process
if (app == null && !permissionsReviewRequired) {
//真正啟動進程
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
......
// Case 2.2.1 進程啟動失敗, stop service
bringDownServiceLocked(r);
return msg;
}
......
}
// Case 2.2.2 進程啟動成功,把Service添加到mPendingServices中
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
return null;
}
bringUpServiceLocked 分幾種情況處理:
1 如果Service已經啟動過,調sendServiceArgsLocked發送start請求
2 如果Service還未啟動過, 查詢Serviced對應進程,看看進程是否已經啟動
2.1 進程已啟動, 調用realStartServiceLocked,創建Service
2.2 進程未啟動, 調用AMS.startProcessLocked啟動進程
2.2.1 進程啟動失敗, 調用bringDownServiceLocked結束Service
2.2.2 進程啟動成功, 將Service信息保存到mPendingServices,等待后面進程回調對其進行處理
這里我們簡單對2.2.2的情況說明一些,進程啟動成功后會調用AMS.attachApplicationLocked,在這里將mPendingServices里屬于該進程的Service啟動,調用的也是realStartServiceLocked
boolean attachApplicationLocked(ProcessRecord proc, String processName)
throws RemoteException {
......
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
realStartServiceLocked(sr, proc, sr.createdFromFg);
}
......
}
也就是說2.1和2.2.2最后都是進入realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
......
try {
......
// 發送create請求
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
......
} finally {
......
}
......
// 發送bind請求,對于startService,bind隊列并沒有增加
// 所以在這種情況下requestServiceBindingsLocked相當于空函數
// 后面分析bindService的時候我們再來看這個函數
requestServiceBindingsLocked(r, execInFg);
......
// 構造start參數StartItem
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null));
}
// 發送start請求,同前面bringUpServiceLocked的Case 1
sendServiceArgsLocked(r, execInFg, true);
......
}
realStartServiceLocked是通知客戶端創建和啟動Service的地方
1 調用ApplicationThead.scheduleCreateService通知客戶端創建服務
2 調用requestServiceBindingsLocked發送bind請求,使用于bindService情況
3 調用sendServiceArgsLocked發送start請求,使用于startService情況
這幾個步驟最后其實都差不多,都是AMS通過ApplicationThread對應的接口通知ActivityThread,以第一種情況為例,省去Binder調用流程,大致如下:
ApplicationThread.scheduleCreateService
--> ActivityThread.H.handleMessage(CREATE_SERVICE)
--> ActivityThread.handleCreateService
private void handleCreateService(CreateServiceData data) {
......
Service service = null;
try {
// 使用反射創建 Service 實例
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
......
}
try {
......
// 調用onCreate回調,然后保存起來
service.onCreate();
mServices.put(data.token, service);
//通知AMS創建完成
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
}
......
} catch (Exception e) {
......
}
}
ActivityThread.handleCreateService創建Service實例,調用onCreate,并保存Service.
sendServiceArgsLocked流程差不多,最后由ActivityThread.handleServiceArgs處理,調用onStartCommon
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
......
while (r.pendingStarts.size() > 0) {
ServiceRecord.StartItem si = null;
try {
......
si = r.pendingStarts.remove(0);
// 記錄start次數
si.deliveryCount++;
int flags = 0;
// 我們可以利用onStartCommand的flag參數來判斷是否是第一次start
if (si.deliveryCount > 1) {
flags |= Service.START_FLAG_RETRY;
}
if (si.doneExecutingCount > 0) {
flags |= Service.START_FLAG_REDELIVERY;
}
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
}
......
}
}
private void handleServiceArgs(ServiceArgsData data) {
// 前面onCreate的時候已經保存了Service在mServices里,現在可以取出
Service s = mServices.get(data.token);
if (s != null) {
try {
......
if (!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
......
}
......
} catch (Exception e) {
......
}
}
}
至此Context.startService完成
bindService
bindService流程同startService差不多,主要多了兩個點, 一個是AMS除了要保存Service信息,還要保存caller app和ServiceConnection信息(用于交互), 這個由ServiceRecord.retrieveAppBindingLocked負責; 另外就是bind完成要使用IActivityManager.publishService接口通知AMS,從而讓ServiceConnection.onServiceConnected執行
下面開始看代碼
Context.bindService
--> ContextImpl.bindServiceCommon
--> AMS.bindService
--> AS.bindServiceLocked
前面的跟startService的差不多, 進入AS.bindServiceLocked
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
......
// 從ServiceMap查找ServiceRecord
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
ServiceRecord s = res.record;
......
try {
......
// 從ServiceRecord查找Client應用信息
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
// 構造ServiceConnection信息
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
......
// 將ServiceConnection保存
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);
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
mServiceConnections.put(binder, clist);
}
clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
......
// bringUpService會通知客戶端onbind
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
......
}
......
} finally {
......
}
}
bindServiceLocked流程比較簡單,先查找或者構造調用者app和ServiceConnection信息,存儲于ServiceRecord, 然后調用bringUpServiceLocked.
bringUpServiceLocked包含了startService和bindService的邏輯,startService的部分前面已經分析過, 這里看bindSerivce有關的流程:
bringUpServiceLocked
--> realStartServiceLocked
--> requestServiceBindingsLocked
// 對ServiceRecord.bindings里的元素依次調用requestServiceBindingLocked
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;
}
}
}
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;
}
requestServiceBindingsLocked則通過Binder最后進入ActivityThread.handleBindService:
ApplicationThread.scheduleBindService
--> ActivityThread.H.handleMessage(BIND_SERVICE)
--> ActivityThread.handleBindService
private void handleBindService(BindServiceData data) {
// 前面Service onCreate的時候已被保存在mServices
Service s = mServices.get(data.token);
......
if (s != null) {
......
try {
if (!data.rebind) {
// 執行onBind回調
IBinder binder = s.onBind(data.intent);
// 通知AMS bind完成
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
......
}
.
} catch (RemoteException ex) {
......
}
} catch (Exception e) {
......
}
}
handleBindService執行Service.onBind,并將結果通過AMS.publishService發送給AMS
AMS.publishService
--> AS.publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
......
try {
if (r != null) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
// 記錄Service和狀態
b.binder = service;
b.requested = true;
b.received = true;
// 找出所有符合的ServiceConnection執行onServiceConnected
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);
if (!filter.equals(c.binding.intent.intent)) {
......
continue;
}
try {
c.conn.connected(r.name, service);
} catch (Exception e) {
......
}
}
}
}
......
}
} finally {
......
}
}
publishService通知ServiceConnection執行onServiceConnected,至此bindService結束
AMS對Service的存儲(stopService/unbindService)
AMS里把所有啟動的Service存放在ActiveServices.mServiceMap, 這是一個SparseArray,可以看成一個Map,其key是調用者的userId,也就是說AMS將系統中所有啟動過的Service按調用者的userId分組. 為了方便分析,這里簡單的將userId等同于appId,即簡單的等同于一個應用.
而ServiceMap本身是存儲了兩個Map mServicesByName
和 mServicesByIntent
, 分別代表顯式和隱式調用的Service信息
class ServiceMap extends Handler {
final ArrayMap<ComponentName, ServiceRecord> mServicesByName = new ArrayMap<>();
final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent = new ArrayMap<>();
......
}
這里我們只取隱式的mServicesByIntent
來分析,它的key是Intent.FilterComparison,可以把它看成是一個override了equals方法的Intent,我們選取最為常見的action作為Intent的代表,當然兩個Intent是否相等還要由 Data, Category 等比較決定,但是不影響我們對這里數據結構的分析. 所以最終我們發現,其實AMS用組合(caller appId, action)作為key來存儲已啟動的Service.
我們假設 Music 應用有一個 PlaybackService, 注冊的action有兩個.
<service android:name="com.haha.music.PlaybackService">
<intent-filter>
<action android:name="com.haha.music.action.PLAYBACK1" />
</intent-filter>
<intent-filter>
<action android:name="com.haha.music.action.PLAYBACK2" />
</intent-filter>
</service>
現在在Settings和Video兩個應用分別startService,其中Settings有兩處,用了兩個不同的action啟動
// In Settings process
startService(new Intent("com.haha.music.action.PLAYBACK1"));
...
startService(new Intent("com.haha.music.action.PLAYBACK2"));
// In Video process
startService(new Intent("com.haha.music.action.PLAYBACK1"));
最后內存里的情況如下:
了解了ServiceMap的結構之后,我們看startService/stopService時對Service的查找, 就是retrieveServiceLocked函數
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal) {
ServiceRecord r = null;
// 得到當前用戶的userId
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
// 從ServiceMap中查找
ServiceMap smap = getServiceMap(userId);
final ComponentName comp = service.getComponent();
if (comp != null) {
r = smap.mServicesByName.get(comp);
}
if (r == null && !isBindExternal) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
r = smap.mServicesByIntent.get(filter);
}
// 如果Map里找不到則要開始構造這個ServiceRecord并保存起來
if (r == null) {
try {
// PKMS根據參數得到對應Pkg中Serivce的ResolveInfo
ResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service,
resolvedType, ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
r = smap.mServicesByName.get(name);
if (r == null && createIfNeeded) {
......
r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
smap.mServicesByName.put(name, r);
smap.mServicesByIntent.put(filter, r);
......
}
} catch (RemoteException ex) {
......
}
}
if (r != null) {
return new ServiceLookupResult(r, null);
}
return null;
}
AS.retrieveServiceLocked其實就是從mServiceMap里查找對應的ServiceRecord,如果找不到就構造一個. 到此,這里的數據對startService/stopService已經夠用.
但是對bindService/unbindService, AMS還得記錄Client和ServiceConnection,這個由ServiceRecord.bindings
和ServiceRecord.connections
來負責. 通過簡化, 我們可以認為ServiceRecord.bindings用來存儲調用者應用信息, key為action, value為caller app信息(AppBinderRecord)隊列. ServiceRecord.connections存儲ServiceConnection, key為ServiceConnection在AMS的代表(binder). 這部分邏輯主要在retrieveAppBindingLocked和bindServiceLocked
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
IntentBindRecord i = bindings.get(filter);
if (i == null) {
i = new IntentBindRecord(this, filter);
bindings.put(filter, i);
}
AppBindRecord a = i.apps.get(app);
if (a != null) {
return a;
}
a = new AppBindRecord(this, i, app);
i.apps.put(app, a);
return a;
}
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
......
try {
......
// 從ServiceRecord查找Client應用信息
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
// 構造ServiceConnection信息
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
......
// 將ServiceConnection保存在ServiceRecord.connections
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);
// 除了每個ServiceRecord保存著自己的connection隊列
// ActiveServices里還有一個所有connection的隊列
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
mServiceConnections.put(binder, clist);
}
clist.add(c);
......
} finally {
......
}
}
上面是屬于Service/ServiceConnection/Caller app的創建和保存,位于startService/bindService.
下面我們看看它們的刪除, 這個位于stopService和unbindService
private void stopServiceLocked(ServiceRecord service) {
......
// 設置 startRequested 標志
service.startRequested = false;
......
// 注意這里的最后兩個參數值,都是false
bringDownServiceIfNeededLocked(service, false, false);
}
// unbindService調用流程:
// AMS.unbindService --> AS.unbindServiceLocked --> AS.removeConnectionLocked
void removeConnectionLocked(
ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
......
AppBindRecord b = c.binding;
......
// remove ServiceRecord.connectins里的記錄
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
s.connections.remove(binder);
}
}
......
// remove ActiveServices.mServiceConnections里的記錄
clist = mServiceConnections.get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
mServiceConnections.remove(binder);
}
}
if (!c.serviceDead) {
......
// 調用onUnbind
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
......
s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
......
}
}
if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
boolean hasAutoCreate = s.hasAutoCreateConnections();
......
// 最后調用bringDownServiceIfNeededLocked, 注意這里最后兩個參數
// 第一個為true, 第二個為s.hasAutoCreateConnections()
bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
}
}
}
stopService相對比較簡單,就是設置一下startRequested為false, 然后調用bringDownServiceIfNeededLocked, 注意這里傳入的最后兩個參數值,都是false
unbindService先找到要unbind的ServiceConnection, 調用removeConnectionLocked對其內部各種數據清理(主要是ServiceRecord.bindings, connections, ActiveServices.mServiceConnections), 然后使用ApplicationThread.scheduleUnbindService通知客戶端onUnbind, 最后也是調用bringDownServiceIfNeededLocked, 注意這里傳入的最后兩個參數值,但是其傳入的是true和s.hasAutoCreateConnections()
可以看到stop/unbind最終都會調用bringDownServiceIfNeededLocked
private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
boolean hasConn) {
if (isServiceNeeded(r, knowConn, hasConn)) {
return;
}
......
bringDownServiceLocked(r);
}
如果isServiceNeeded返回false就會執行bringDownServiceLocked,先看一下這個函數
private final void bringDownServiceLocked(ServiceRecord r) {
......
final ServiceMap smap = getServiceMap(r.userId);
smap.mServicesByName.remove(r.name);
smap.mServicesByIntent.remove(r.intent);
......
if (r.app != null) {
......
if (r.app.thread != null) {
......
try {
......
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
......
}
} else {
......
}
} else {
......
}
......
}
bringDownServiceLocked其實就是清理ActiveServices.mServiceMap里的數據,并且通知Service.onDestory
回過頭看isServiceNeeded
private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn, boolean hasConn) {
// Are we still explicitly being asked to run?
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;
}
//ServiceRecord.java
public boolean hasAutoCreateConnections() {
for (int conni=connections.size()-1; conni>=0; conni--) {
// unbindService --> removeConnectionLocked的時候會remove對應的connection
// 所以剩下的還在connections中的就是還沒unbind的
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;
}
結合上面stopService和unbindService調用時傳給它的參數值, 我們知道isServiceNeeded的邏輯等同于r.startRequested && r.hasAutoCreateConnections()
r.startRequested在startService時置為true, stopService置為false, r.hasAutoCreateConnections()則在沒有client bindService的時候返回false, 所以就是說bringDownServiceLocked,即Service.onDestory只有在調用過stopService(或者沒調用過startService)和所有connection都被unbind之后才會被執行
Background Service in Android O
現在Android對 Background Task(Service/Broadcast等)的限制已經越來越嚴格, 對Service而言,當進程處于后臺時, Android O 已經不允許 startService, 上面是N的代碼,我們簡單看下 O preview的流程
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, final int userId)
throws TransactionTooLargeException {
......
if (!r.startRequested && !fgRequired) {
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false);
}
......
}
startServiceLocked 關于對于Service是否允許后臺start的判斷在O上有了變化,這里使用的是AMS.getAppStartModeLocked
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
......
if (uidRec == null || alwaysRestrict || uidRec.idle) {
......
if (ephemeral) {
......
} else {
......
//alwaysRestrict為false,所以調用appServicesRestrictedInBackgroundLocked
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
//當Service的startMode不是Normal
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
......
if (proc != null &&
//并且進程不是處于后臺
!ActivityManager.isProcStateBackground(proc.curProcState)) {
//那么就把startMode改為Normal
return ActivityManager.APP_START_MODE_NORMAL;
}
}
//否則返回原模式
return startMode;
}
}
......
}
這里fgRequired參數在Context.startService時為false, 而Context.startForegroundService為true, 所以這里調用的是appRestrictedInBackgroundLocked
// 處于 Persistent/background whitelist/device idle white list 這三種情況下的 app 不受 background 限制
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
......
}
appRestrictedInBackgroundLocked對處于Persistent/background whitelist/device idle white list下的apps不設置Background limit,而其他的當targetSdkVersion >= O時, 則會限制
在Developer Guide的Background Execution Limits文章中, Google 建議是用 JobScheduler 來取代 Background task. 另外目前 Android O preview 版本還提供了 Context.startForegroundService, 按照其說法調用該接口創建Service后,需要在5秒(ANR interval)內調用 Service.startForeground(),否則 Service 就會被 stop 并且應用進程將 ANR.
目前 Android O 處于 preview 階段,不知道后續還會不會出現變化,因為看網上有一些文章提到 NotificationManager.startServiceInForeground(), 但是官網上已經找不到該接口了