Service的啓動概述
本章我們來分析Service的啓動過程。
Service生命週期
先來看下Service的生命週期:
startService和bindService啓動的服務生命週期略有不同。
Service啓動方式
我們啓動一個Serivce服務的時候,可以通過3種方式來啓動:
- startService()方法,啓動一個Service。
- bindService()方法,使用bind的方式啓動一個Service。
- startForegroundService()方法,啓動一個前臺服務。
這幾種啓動方式,定義在Activity的父類型ContextWrapper類中,它們都通過調用mBase來執行具體實現,這裏的mBase,就是一個ContextImpl實例對象。
startService啓動——Client進程端
ContextImpl的相關實現
service啓動的幾個方法:
/frameworks/base/core/java/android/app/ContextImpl.java
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
@Override
public ComponentName startForegroundService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, true, mUser);
}
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
}
邏輯解析:
- startService和startForegroundService都調用了startServiceCommon方法,只是第二個參數不同(前臺服務是true)。
- bindService實現稍有不同,這裏調用了bindServiceCommon方法。
我們重點來分析startService的後續流程。
來看startServiceCommon方法:
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
邏輯解析:
- 驗證Intent相關設置,如果Service是隱式啓動,則在Android L之上會拋出錯誤,在小於該版本輸出提示日誌但不報錯。
- 判斷是否將要離開當前進程,並且設置Intent相關屬性。
- 調用AMS的startService方法來啓動Service,在此之後,代碼的執行由Client端切換到了系統服務AMS進程。
startService啓動——Server進程端
AMS的startService方法
AMS的startService方法:
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService"); //判斷是否是隔離的程序,如果是,則不允許執行,拋出錯誤
……
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}
邏輯解析:
- 執行一些簡單的驗證工作,例如判斷是否是隔離程序,調用者的包名是否爲null等。
- 調用mServices.startServiceLocked執行啓動過程。這裏的mServices是ActiveServices的一個實例,用來管理Serivice的。
ActiveServices的startServiceLocked方法
ActiveServices的startServiceLocked方法:
/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
final int userId, boolean allowBackgroundActivityStarts)
throws TransactionTooLargeException {
……
//判斷當前調用進程是否屬於前臺進程組(進程組優先級!= ProcessList.SCHED_GROUP_BACKGROUND)
final boolean callerFg;
if (caller != null) {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + callingPid
+ ") when starting service " + service);
}
callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
//調用retrieveServiceLocked方法來獲取一個ServiceLookupResult對象。
ServiceLookupResult res =
retrieveServiceLocked(service, null, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false, false);
……
ServiceRecord r = res.record; //從ServiceLookupResult對象中,取出ServiceRecord對象,以進行後續操作。
……
//處理、校驗啓動權限等相關的邏輯,並且判斷將要啓動的服務是否是前臺服務等。
……
// At this point we've applied allowed-to-start policy based on whether this was
// an ordinary startService() or a startForegroundService(). Now, only require that
// the app follow through on the startForegroundService() -> startForeground()
// contract if it actually targets O+.
if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
if (DEBUG_BACKGROUND_CHECK || DEBUG_FOREGROUND_SERVICE) {
Slog.i(TAG, "startForegroundService() but host targets "
+ r.appInfo.targetSdkVersion + " - not requiring startForeground()");
}
fgRequired = false; //fgRequired表示是否需要執行startForeground來啓動Service。
}
//對是否需要啓動前彈框進行判斷
NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
……
……
if (fgRequired) { //啓動前臺服務
// We are now effectively running a foreground service.
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
r.lastActivity);
}
mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
}
……
if (allowBackgroundActivityStarts) { //如果允許從後臺Activity啓動
r.whitelistBgActivityStartsOnServiceStart();
}
//執行常規的啓動流程
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
邏輯解析:
- 判斷當前調用者是否屬於前臺進程組(進程組優先級!= ProcessList.SCHED_GROUP_BACKGROUND)。
- 調用retrieveServiceLocked方法來獲取一個ServiceLookupResult對象。retrieveServiceLocked首先從緩存列表中查找是否已經存在該Service記錄,如果不存在,則從包資源裏解析獲取,最終生成一個含有Service信息的ServiceLookupResult對象。
- 從ServiceLookupResult對象中,取出ServiceRecord對象,以進行後續操作。
- 處理、校驗啓動權限等相關的邏輯,並且判斷將要啓動的服務是否是前臺服務等。
- fgRequired表示是否需要執行startForeground來啓動Service。
- 對是否需要啓動前彈框進行判斷。
- 如果是前臺服務,則執行啓動前臺服務。
- 調用startServiceInnerLocked執行常規的啓動流程。
ActiveServices的startServiceInnerLocked方法
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);//更新ServiceRecord的ServiceState
}
r.callStart = false;
StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid, r.name.getPackageName(),
r.name.getClassName(), StatsLog.SERVICE_STATE_CHANGED__STATE__START);
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();//用於耗電統計,開啓運行的狀態
}
//service啓動的核心方法
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, 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() + mAm.mConstants.BG_START_TIMEOUT;
if (DEBUG_DELAYED_SERVICE) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
} else if (DEBUG_DELAYED_STARTS) {
Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
}
if (first) {
smap.rescheduleDelayedStartsLocked();//執行延遲啓動
}
} else if (callerFg || r.fgRequired) {
smap.ensureNotStartingBackgroundLocked(r);//清楚map數據
}
return r.name;
}
邏輯解析:
- 調用service的bringUpServiceLocked方法來啓動Service。
- 當啓動完成的時候設置啓動超時時間。
- 啓動成功之後將ServiceRecord加入到smap中的mStartingBackground中。
- 如果是第一次啓動則處理延遲啓動相關邏輯。
ActiveServices的bringUpServiceLocked方法
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
if (r.app != null && r.app.thread != null) {//處理Service已經啓動的情況,此時只是發送新的StartItem
sendServiceArgsLocked(r, execInFg, false);
return null;
}
if (!whileRestarting && mRestartingServices.contains(r)) { //如果Service因爲crash被系統重啓,則return
// If waiting for a restart, then do nothing.
return null;
}
……
//正在啓動服務,因此不再處於重新啓動狀態,把service從重啓服務隊列中移除。
if (mRestartingServices.remove(r)) {
clearRestartingIfNeededLocked(r);
}
//確保此服務不再被視爲延遲,設置r.delayed爲false。
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
// 確保擁有該服務的user已經啓動,如果未啓動,則終止操作,並執行清理工作。
if (!mAm.mUserController.hasStartedUserState(r.userId)) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
// 服務正在啓動,設置package停止狀態爲false
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.packageName, false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
}
//判斷當前服務是否需要在隔離進程中啓動。(如果在Manifest中設置了隔離進程的標誌位則該值等於標誌位的值)
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
ProcessRecord app;
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.longVersionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg); //真正啓動Service的地方
return null;
}
……
}
} else {
……
}
//處理進程沒有啓動的情況
if (app == null && !permissionsReviewRequired) {
//啓動service所要運行的進程
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingRecord, 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保存待啓動服務,當進程啓動後,會重新啓動該服務
mPendingServices.add(r);
}
if (r.delayedStop) {//服務還未完成啓動,就收到結束請求時,會直接停止該服務
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (in bring up): " + r);
stopServiceLocked(r);//停止服務
}
}
return null;
}
邏輯解析:
- 處理Service已經啓動的情況,此時只是發送新的StartItem,調用service.onStartCommand()過程。
- 如果Service因爲crash被系統重啓,則return。
- 正在啓動服務,因此不再處於重新啓動狀態,把service從重啓服務隊列中移除。
- 確保此服務不再被視爲延遲,設置r.delayed爲false。
- 確保擁有該服務的user已經啓動,如果未啓動,則終止操作,並執行清理工作。
- 服務正在啓動,設置package停止狀態爲false。
- 判斷當前服務是否需要在隔離進程中啓動。(如果在Manifest中設置了隔離進程的標誌位則該值等於標誌位的值)
- 如果不是在隔離進程中啓動,則調用realStartServiceLocked(r, app, execInFg); 真正啓動Service。
- 如果進程沒有啓動,啓動service所要運行的進程。
- mPendingServices保存待啓動服務,當進程啓動後,會重新啓動該服務。
ActiveServices的realStartServiceLocked方法
即將在Client中創建並啓動Service實例對象,從bindService()啓動的Service也會調用這個方法。
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
……
這裏主要更新進程和ServiceRecord的一些信息
bumpServiceExecutingLocked(r, execInFg, "create");//將service和進程關聯起來(更新ServiceRecord中的信息)
//更新進程對應的優先級
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
boolean created = false;
try {
……
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);//強制更改進程的狀態爲ActivityManager.PROCESS_STATE_SERVICE
//調用Client端ApplicationThread對象的實例方法scheduleCreateService來創建Service。
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
r.postNotification();
created = true;
} catch (DeadObjectException e) {
Slog.w(TAG, "Application dead when creating service " + r);
mAm.appDiedLocked(app);
throw e;
} finally {
if (!created) {
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying); //執行清理工作
// Cleanup.
if (newService) {
app.services.remove(r);
r.setProcess(null);
}
// Retry.
if (!inDestroying) {
scheduleServiceRestartLocked(r, false);//嘗試重新啓動服務
}
}
}
if (r.whitelistManager) {
app.whitelistManager = true;
}
//如果調用過bindService,則會調用requestServiceBindingsLocked()執行onBind方法。
requestServiceBindingsLocked(r, execInFg);
//如果客戶端Bind Service成功,按需更新服務端進程優先級
updateServiceClientActivitiesLocked(app, null, true);
if (newService && created) {
app.addBoundClientUidsOfNewService(r);
}
//如果service已經啓動,並且符合條件,則方法onStartCommand() 將被調用
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null, 0));
}
sendServiceArgsLocked(r, execInFg, true);
if (r.delayed) {//如果service是延時啓動的則將其從mDelayedStartList中移除
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
if (r.delayedStop) { //如果service被主動要求停止那麼調用
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (from start): " + r);
stopServiceLocked(r);
}
}
}
邏輯解析:
- 更新進程和ServiceRecord的一些信息,並將service和進程關聯起來(更新ServiceRecord中的信息)。
- 更新進程對應的優先級。
- 強制更改進程的狀態爲ActivityManager.PROCESS_STATE_SERVICE。
- 調用Client端ApplicationThread對象的實例方法scheduleCreateService來創建Service。
- 如果服務創建失敗,則執行清理工作,並且嘗試重新啓動服務。
- 如果調用過bindService,則會調用requestServiceBindingsLocked()執行onBind方法,成功後,更新進程優先級。
- 如果service已經啓動,並且符合條件,則方法onStartCommand() 將被調用。
- 如果service是延時啓動的則將其從mDelayedStartList中移除。
- 如果service被主動要求停止那麼調用。
startService啓動——回到Client端
在ActiveServices的realStartServiceLocked方法中,調用了Client端ApplicationThread對象的實例方法scheduleCreateService來創建Service。
ApplicationThread對象的scheduleCreateService方法:
android.app.ActivityThread.java
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
這裏發生給Handler一個CREATE_SERVICE的消息,我們直接來看該消息的處理方法。
private void handleCreateService(CreateServiceData data) {
//對將要進行GC進行處理。
unscheduleGcIdler();
//獲得這個進程對應的LoadedApk對象
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//通過反射調用,創建一個Serice對象
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
……
}
try {
……
//創建Service的ContextImpl對象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//獲取進程對應的Application對象。
Application app = packageInfo.makeApplication(false, mInstrumentation);
//調用service對象的attach方法進行初始化設置
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
//調用Service的onCreate方法。
service.onCreate();
mServices.put(data.token, service);//添加服務到mServices中
try {
//通知AMS service啓動成功,進行取消超時消息等操作
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
邏輯解析:
- 對將要進行GC進行處理,跳過。
- 獲得這個進程對應的LoadedApk對象。
- 通過反射調用,創建一個Serice對象。
- 創建Service的ContextImpl對象。
- 獲取進程對應的Application對象。
- 調用service對象的attach方法進行初始化設置。
- 調用Service的onCreate方法。
- 添加服務到mServices中。
- 通知AMS service啓動成功,進行取消超時消息等操作。
到了這裏,Service已經完成了啓動過程。
Service的onStartCommand調用過程
那麼Service的onStartCommand又是怎麼樣的調用流程呢?
它的起點是ActiveServices的sendServiceArgsLocked方法:
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
……
try {
r.app.thread.scheduleServiceArgs(r, slice);
}
……
}
這裏調用了ApplicationThread對象的scheduleServiceArgs方法,該方法又給ActivityThread對象的Handler對象發送了H.SERVICE_ARGS消息,最終調用handleServiceArgs方法來處理該消息。
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess();
}
int res;
if (!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
……
}
}
這裏調用了Servcie對象的onStartCommand方法。
到了這裏,Service的啓動邏輯相關的重要過程也就分析完了。
總結
通過本文分析,我們對startSerivce的啓動流程已經瞭解了,這裏來做一個簡單的總結:
- 可以通過3種方式來啓動一個Serivce服務:startService()方法,啓動一個Service;bindService()方法,使用bind的方式啓動一個Service;startForegroundService()方法,啓動一個前臺服務。
- 上述3種啓動方法的是在ContextImpl中實現的。
- startService啓動在Client進程端發起,並且進行Intent相關設置,以及一些校驗工作。
- 然後進入到Server端(AMS所在進程),AMS使用ActiveServices類來對服務進行管理,系統服務端的主要處理工作都是在ActiveServices中進行的,最終調用到ActiveServices的realStartServiceLocked方法。
- ActiveServices的realStartServiceLocked方法調用了Client端ApplicationThread對象的實例方法scheduleCreateService來創建Service(回到Client端來進行Service的創建,這裏的Client是Service的所在進程,不一定是啓動時發起請求的Client進程了,這裏需要注意)。
- ActiveServices的sendServiceArgsLocked方法最終調用了Service的onStartCommand方法。