Android笔记-service启动过程分析:startService源码分析
前言:
Service的启动流程将会分为一个系列来讲述。
本系列开始将分析Service的启动过程。
看这个系列文章之前你所需要知道的知识点:
1. 熟悉service的基本用法。
2. 了解bind机制,知道android的客户端和AMS间通信流程。
3. 最好学习过activity的启动流程。
本系列将涉及到以下一些分支:
startService源码分析
bindService源码分析、startService和bindService区别
第二次startService为什么没有调用onCreate
为什么bindService和startService同时调用后需要同时调用unBind和stop才能使服务停止。
前台Service原理
今天这一篇将讲述startService源码分析:
service启动过程分析
startService和bindService流程图
了解Context
Context分析
开始分析startService源码我们之前先了解下Context。
context是一个抽象类,它定义了一些通用方法。
最常见的有:
startActivity()//启动Activity
startService() //启动Service
stopServic() //停止Service
unbindService()//取消绑定Service
bindService() //绑定Service
registerReciver()// 注册广播
sendBroadCastReciver()// 发送广播
getResources() //获取资源
..... 还有好多
我们看到四大组件基本操作都在这个类里面定义好了,还有一些方法也是我们日常开发中用到的。
今天我们要看的是startService这个方法。
先看一下源码:
public abstract ComponentName startService(Intent service);
很简单定义了一个抽象方法,需要传入一个Intent。
这个方法的实现在哪呢?我们可以从Activity里面的startActivity追溯一下。
从源码里面发现Activity里面并实现没有startService方法。
而在ContextWrapper里面实现了,方法如下:
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
这个mBase是什么?其实它也是一个Context。他是那里来的呢?
熟悉Activity启动流程的小伙伴一定知道最终启动Activity时候,Activity会先调用一下attach方法,没错这个mBase就是在这时候赋值的:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);//给mBase赋值
...
}
activity中的attach方法的第一参数就是赋值给mBase的。我们再看它是什么,从源码中看到,其实他是一个ContextImpl。如下:
ContextImpl appContext = createBaseContextForActivity(r);
createBaseContextForActivity具体做内容可以暂时不看,但我们可以知道,原来真正实现startService的类是ContextImpl。
小结:
Context:抽象类,定义了开发中常用方法,如四大组件的使用方法。
ContextImpl:继承Context,真正实现Context中的方法。
ContextWrapper:继承Context,包含一个ContextImpl。是包装类,是Activity、Service、Application的父类。
他们的关系可以看类图:
Context相关类图
startService源码分析
onCreate流程源码分析
下面我们就真正进入ContextImpl的startService学习吧。
startService源码如下:
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
startServiceCommon源码如下:
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(//进程间通信,进入AMS流程
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();
}
}
我们看到这里用到了跨进程通信,最终会调用AMS的startService方法。(这里涉及的跨进程通信机制不详细说明了,我在startActivity源码分析一文中有详细说明,感兴趣的可以自行了解)。
再看AMS中的startService:
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
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;
}
}
mServices是一个ActiveServices类,该类用来辅助处理Service在服务端进程中的开始、绑定和结束。
startServiceLocked源码如下:
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, final int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
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=" + Binder.getCallingPid()
+ ") when starting service " + service);
}
callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
if (res == null) {
return null;
}
if (res.record == null) {
return new ComponentName("!", res.permission != null
? res.permission : "private to package");
}
ServiceRecord r = res.record;
if (!mAm.mUserController.exists(r.userId)) {
Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}
if (!r.startRequested) {
final long token = Binder.clearCallingIdentity();
try {
// Before going further -- if this app is not allowed to run in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.checkAllowBackgroundLocked(
r.appInfo.uid, r.packageName, callingPid, true);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
return null;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
// If permissions need a review before any of the app components can run,
// we do not start the service and launch a review activity if the calling app
// is in the foreground passing it a pending intent to start the service when
// review is completed.
if (Build.PERMISSIONS_REVIEW_REQUIRED) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
callingUid, service, callerFg, userId)) {
return null;
}
}
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
//注意ServiceRecord的pendingStarts,后面需要用到
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
final ServiceMap smap = getServiceMap(r.userId);
boolean addToStarting = false;
if (!callerFg && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
// to delay the start if there are already other background services
// that are starting. This is to avoid process start spam when lots
// of applications are all handling things like connectivity broadcasts.
// We only do this for cached processes, because otherwise an application
// can have assumptions about calling startService() for a service to run
// in its own process, and for that process to not be killed before the
// service is started. This is especially the case for receivers, which
// may start a service in onReceive() to do some additional work and have
// initialized some global state as part of that.
if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
+ r + " in " + proc);
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) {
// We slightly loosen when we will enqueue this new service as a background
// starting service we are waiting for, to also include processes that are
// currently running other services or receivers.
addToStarting = true;
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Not delaying, but counting as bg: " + r);
} else if (DEBUG_DELAYED_STARTS) {
StringBuilder sb = new StringBuilder(128);
sb.append("Not potential delay (state=").append(proc.curProcState)
.append(' ').append(proc.adjType);
String reason = proc.makeAdjReason();
if (reason != null) {
sb.append(' ');
sb.append(reason);
}
sb.append("): ");
sb.append(r.toString());
Slog.v(TAG_SERVICE, sb.toString());
}
} else if (DEBUG_DELAYED_STARTS) {
if (callerFg) {
Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="
+ callingUid + " pid=" + callingPid + "): " + r);
} else if (r.app != null) {
Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);
} else {
Slog.v(TAG_SERVICE,
"Not potential delay (user " + r.userId + " not started): " + r);
}
}
//注意:调用startServiceInnerLocked
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
注意ServiceRecord的startRequested,该属性和pendingStarts决定后面是否需要执行onstart方法。
经过一系列ActiveServices类中方法调用,我们可以看时序图,最终调用到的是:realStartServiceLocked方法:
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
}
if (DEBUG_MU)
Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ ", ProcessRecord.uid = " + app.uid);
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean newService = app.services.add(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
mAm.updateOomAdjLocked();
boolean created = false;
try {
if (LOG_SERVICE_START_STOP) {
String nameTerm;
int lastPeriod = r.shortName.lastIndexOf('.');
nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
}
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);//注意这里
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.app = null;
}
// Retry.
if (!inDestroying) {
scheduleServiceRestartLocked(r, false);
}
}
}
if (r.whitelistManager) {
app.whitelistManager = true;
}
requestServiceBindingsLocked(r, execInFg);
//这里会调用onBind方法
updateServiceClientActivitiesLocked(app, null, true);
// If the service is in the started state, and there are no
// pending arguments, then fake up one so its onStartCommand() will
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null, 0));
}
//这里后续会掉用onStartCommend方法
sendServiceArgsLocked(r, execInFg, true);
if (r.delayed) {
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) {
// 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和ProcessRecord这两个类分别用来保存服务相关信息和进程相关信息。其中ServiceRecord类似ActivityRecord,而ProcessRecord这个类在startActivity流程中也看到过,这里面的一个属性thread即从客户端传过来的ApplicationTread。使用:
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
方法通知客户端启动Service。
客户端ApplicationThread中调用了scheduleCreateService方法,源码如下:
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);
}
ActivityThread中的具体调用流程和startActivity中流程相似,不详细分析了。
从时序图中看到:最终实现启动Service方法是:ActivityThread的handleCreateService方法:
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//反射出一个Service
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
//这里新建了一个ContextImpl,作为Service的mBase,对比前面Context谈到过Activity里的mBase创建过程,其实是差不多的。
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();
//一个列表,用来保存已启动的Service
mServices.put(data.token, service);
try {
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);
}
}
}
我们需要知道:这个方法用反射的方式创建了一个Service并调用了Service的attac方法和onCreate方法,并把这个Service放到了一个ArrayMap中,如下:
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
该mServices在ActivityThread初始化后被初始化,而且是final修饰,不可以重新赋值。是用来存储已启动service的。
onStartCommand流程解析
到这里onCreate方法已经执行完了,我们再看看onStartCommand在哪执行的。在上面的realStartServiceLocked方法中我们看到:sendServiceArgsLocked(r, execInFg, true);方法,该方法后续会执行onStartCommand。我们看看源码:
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 = null;
try {
si = r.pendingStarts.remove(0);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Sending arguments to: "
+ r + " " + r.intent + " args=" + si.intent);
if (si.intent == null && N > 1) {
// If somehow we got a dummy null intent in the middle,
// then skip it. DO NOT skip a null intent when it is
// the only one in the list -- this is to support the
// onStartCommand(null) case.
continue;
}
si.deliveredTime = SystemClock.uptimeMillis();
r.deliveredStarts.add(si);
si.deliveryCount++;
if (si.neededGrants != null) {
mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
si.getUriPermissionsLocked());
}
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
mAm.updateOomAdjLocked(r.app);
}
int flags = 0;
if (si.deliveryCount > 1) {
flags |= Service.START_FLAG_RETRY;
}
if (si.doneExecutingCount > 0) {
flags |= Service.START_FLAG_REDELIVERY;
}
//这里调用客户端的onStartCommend方法
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (TransactionTooLargeException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
+ si.intent);
caughtException = e;
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take care of this.
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
caughtException = e;
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception", e);
caughtException = e;
}
if (caughtException != null) {
// Keep nesting count correct
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
if (caughtException instanceof TransactionTooLargeException) {
throw (TransactionTooLargeException)caughtException;
}
break;
}
}
}
r.pendingStarts如果不为空则会执行:r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);方法,r.pendingStarts是一个StartItem列表,保存了ServiceRecord的信息,其构造函数:
StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
ActivityManagerService.NeededUriGrants _neededGrants) {
sr = _sr;
taskRemoved = _taskRemoved;
id = _id;
intent = _intent;
neededGrants = _neededGrants;
}
由于在sendServiceArgsLocked方法中判断了r.pendingStarts的size,所以需要知道r.pendingStarts是怎么赋值的,其实在一开始的startServiceLocked方法中,就给pendingStarts增加了记录,再回头看看源码:
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, final int userId)
throws TransactionTooLargeException {
...
r.lastActivity = SystemClock.
r.startRequested = true;
r.delayedStop = false;
//给pendingStarts增加记录
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
...
}
的确r.pendingStarts已经被加入一条记录,其size大于0,所以在sendServiceArgsLocked会执行r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);方法。
这里我们可以猜想:bindService流程应该没有给pendingStarts赋值,才不会执行onStartCommand方法的。我们后面分析bindService时候可以看到的确没有赋值,而是用另外一个属性判断的,这里先卖个关子。想知道怎么判断的可以看看下面一篇文章。
至于r.app.thread.scheduleServiceArg如何执行到onStartCommand基本和执行onCreate方法的流程一致,也用到了bind跨进程通信,就不具体说明了。可以自行看源码。
本编到此告一段落了。
总结
做一个总结:
1. Activity、Service都继承了ContextWrapper(Activity继承了它的子类ContextThemeWrapper),ContextWrapper包含了一个mBase(ContextImpl), ContextWrapper和ContextImpl都继承了Context,在Activity、Service中调用startService、bindService等服务相关方法,用的是ContextWrapper中的mBase(ContextImpl),所以最终实现类其实是ContextImpl。
2. startActivity流程如下:
3. 控制是否能执行到客户端中onStartCommend方法的关键参数是:ServiceRecord 中的pendingStarts。该参数是一个StartItem列表,保存了ServiceRecord的信息。通过循环获取该列表中的每项,通知客户端执行onStarCommend方法。
4. 猜想bindService流程中没有给r.pendingStarts赋值而是用了另外的参数来判断是否之心onBind方法的。
下篇文章我们将通过对比bindService和startService的不同来解析一下bindService。
上一篇: React diff
推荐阅读
-
Android Service的启动过程分析
-
Tomcat源码分析 (六)----- Tomcat 启动过程(一)
-
Android Service的启动流程源码分析
-
分析Android Activity的启动过程
-
Bootstrap初始化过程源码分析--netty客户端的启动
-
【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | LoadedApk 源码分析 )
-
Flutter Android启动源码分析(二)
-
Android init service启动流程分析
-
Hadoop源码学习笔记之NameNode启动流程分析一:源码环境搭建和项目模块及NameNode结构简单介绍
-
Android 系统服务TelecomService启动过程原理分析