Android中Service在新进程中的启动流程3
目录
1、AMS调用客户端onCreate前的准备工作
2、AMS调用客户端onCreate方法
3、AMS调用客户端的onBind方法
4、AMS调用客户端onStart前的准备
5、AMS调用客户端onStart方法
还是先放上Service启动流程概览图,如下:
上一篇文章, 我们分析到了第五步scheduleCreateService,该方法位于AMS的realStartServiceLocked方法调用中,我们说过当新进程启动之后,会通过AMS调用attachApplication,该方法通过最后调用了realStartServiceLocked,所以我们继续从该方法开始分析,代码如下:
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app) throws RemoteException {
//...
//1、保存相关变量
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
app.services.add(r);
//2、仔细看看?
bumpServiceExecutingLocked(r, "create");
updateLruProcessLocked(app, true, true);
boolean created = false;
try {
//3、调用客户端的scheduleCreateService方法
ensurePackageDexOpt(r.serviceInfo.packageName);
app.thread.scheduleCreateService(r, r.serviceInfo);
r.postNotification();
created = true;
} finally {
//...
}
//4调用绑定方法
requestServiceBindingsLocked(r);
//5、设置相关变量
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.lastStartId++;
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
}
//6、调用onStart
sendServiceArgsLocked(r, true);
}
这里再深入分析下该方法,主要涉及以下几点:
- r.app = app:把ProcessRecord保存到ServiceRecord中,表示服务ServiceRecord服务r运行于ProcessRecord对象app中。app.services.add(r):把服务ServiceRecord服务对象r保存到ProcessRecord对象的services集合中,表示ProcessRecord进程中运行了哪些服务。
- bumpServiceExecutingLocked(r, "create"):这里待会分析,主要是调用服务onCreate前,AMS需要做些工作。
- scheduleCreateService:该方法最后会调用客户端服务的onCreate方法。
- requestServiceBindingsLocked(r):该方法最后会调用客户端服务的onBind方法。
- 调用客户端onStart之前的一些准备工作。
- 调用客户端的onStart
下面我们一步步分析以上几点(第一点以及比较明确,不再说明)
1、AMS调用客户端onCreate前的准备工作
我们知道在服务的onCreate方法中执行太久会导致系统弹窗ANR,这是如何实现的呢?玄机就在这里,我们分析下调用bumpServiceExecutingLocked(r, "create"),代码如下:
private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
//...
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0 && r.app != null) {
if (r.app.executingServices.size() == 0) {
Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
msg.obj = r.app;
mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
}
r.app.executingServices.add(r);
}
r.executeNesting++;
//主要用于计时,看服务是否允许超时,超时则会ANR
r.executingStart = now;
}
发送了消息SERVICE_TIMEOUT_MSG,延时SERVICE_TIMEOUT,时间是20s;所以onCreate执行的时间最多及时20s,我们看看超时的处理,也是位于AMS中,跟踪到逻辑如下:
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(this) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT;
Iterator<ServiceRecord> it = proc.executingServices.iterator();
ServiceRecord timeout = null;
long nextTime = 0;
while (it.hasNext()) {
ServiceRecord sr = it.next();
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
anrMessage = "Executing service " + timeout.shortName;
} else {
Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT);
}
}
if (anrMessage != null) {
appNotResponding(proc, null, null, anrMessage);
}
}
遍历ProcessRecord对象中的集合executingServices,看是否有超时的服务,如果有最后会调用appNotResponding,也就弹出了ANR弹窗。
2、AMS调用客户端onCreate方法
现在来看看app.thread.scheduleCreateService(r, r.serviceInfo)如何调用到客户端的onCreate方法呢。
我们知道该方法会进入到ActivityThread的scheduleCreateService方法(严格来说是ApplicationThread)中,代码如下:
public final void scheduleCreateService(IBinder token,
ServiceInfo info) {
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
queueOrSendMessage(H.CREATE_SERVICE, s);
}
发送了个CREATE_SERVICE消息,该消息的处理如下:
private final void handleCreateService(CreateServiceData data) {
//...
LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
Service service = null;
try {
//1 加载服务类
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
//...
}
try {
//2 创建ContextImpl对象
ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
Application app = packageInfo.makeApplication(false, mInstrumentation);
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//3 调用服务的onCreate方法
service.onCreate();
mServices.put(data.token, service);
try {
//4 服务执行完通知AMS
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
} catch (Exception e) {
//....
}
}
主要有以下几个关键点:
- 通过loadClass加载我们实现的服务类,比如MyService。
- 创建ContextImpl对象,所以我们在服务Service里面调用的相关Context方法最终都是由这里创建的ContextImpl对象实现的。
- 调用服务的onCreate方法,这是我们需要实现的服务生命周期方法。
- 服务执行完onCreate方法后调用serviceDoneExecuting(
data.token, 0, 0, 0)通知AMS。
主要看看第四点做了啥,serviceDoneExecuting的实现位于AMS中,代码如下:
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
//...
ServiceRecord r = (ServiceRecord)token;
boolean inStopping = mStoppingServices.contains(token);
if (r != null) {
if (r != token) {
//...
return;
}
//1、不会走这里
if (type == 1) {
//...
}
final long origId = Binder.clearCallingIdentity();
//2
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
} else {
//...
}
}
}
参数type为0,所第一处不会进入;继续调用serviceDoneExecutingLocked,代码如下:
public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
//...
r.executeNesting--;
if (r.executeNesting <= 0 && r.app != null) {
//移除正在执行的服务
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//1
mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
}
if (inStopping) {
//移除已经停止的服务
mStoppingServices.remove(r);
r.bindings.clear();
}
updateOomAdjLocked(r.app);
}
}
标记1处移除了延时消息SERVICE_TIMEOUT_MSG ,而该消息正是执行方法bumpServiceExecutingLocked时post的,这里移除掉就不会有ANR产生了。
所以我们是不是已经清楚了为啥Service的onCreate方法耗时太久会产生ANR呢?其实正是因为在调用客户端Service的onCreate之前,AMS悄悄的post了一个延时消息SERVICE_TIMEOUT_MSG,时间是20s,要是客户端的onCreate在20s内执行完了,就会把延时消息移除掉,这样就不会产生ANR了,反之则AMS会弹窗ANR。
3、AMS调用客户端的onBind方法
我们继续分析,接下来是requestServiceBindingsLocked方法调用,代码如下:
private final void requestServiceBindingsLocked(ServiceRecord r) {
Iterator<IntentBindRecord> bindings = r.bindings.values().iterator();
while (bindings.hasNext()) {
IntentBindRecord i = bindings.next();
if (!requestServiceBindingLocked(r, i, false)) {
break;
}
}
}
IntentBindRecord代表了绑定到该服务的客户端,这里遍历绑定了的对象,然后调用方法requestServiceBindingLocked,代码如下:
private final boolean requestServiceBindingLocked(ServiceRecord r,
IntentBindRecord i, boolean rebind) {
//假如服务还没起来,不能调用onBind,直接返回了
if (r.app == null || r.app.thread == null) {
// If service is not currently running, can't yet bind.
return false;
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, "bind");
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (RemoteException e) {
return false;
}
}
return true;
}
套路与调用客户端的onCreate方法是一致的,先调用bumpServiceExecutingLocked方法post一个延时消息,然后调用scheduleBindService,该方法最终会走到ActivityThread的 handleBindService中,代码如下:
private final void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
try {
if (!data.rebind) {//绑定服务
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {//重新绑定
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
//...
}
}
}
根据标识data.rebind判断是重新绑定还是首次绑定,首次绑定会调用onBind(然后调用AMS的publishServie发布服务,读者可以自己分析发布服务做了些啥);重新绑定调用onRebind,然后调用AMS的serviceDoneExecuting方法,从AMS调用onCreate的分析中我们知道方法serviceDoneExecuting的作用就是移除延时消息,避免ANR产生(publishService最后也会调用方法serviceDoneExecuting,所以也不会出现ANR)。
4、AMS调用客户端onStart前的准备
这比较简单,代码注释清楚了,不再重复说明,代码如下:
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app) throws RemoteException {
//....
//在调用onStart前创建了个StartItem并放入集合pendingStarts中
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.lastStartId++;
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
}
sendServiceArgsLocked(r, true);
}
5、AMS调用客户端onStart方法
最后是AMS对onStart方法的调用,sendServiceArgsLocked代码如下:
private final void sendServiceArgsLocked(ServiceRecord r,
boolean oomAdjusted) {
final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
while (r.pendingStarts.size() > 0) {
try {
//1
ServiceRecord.StartItem si = r.pendingStarts.remove(0);
if (si.intent == null) {
continue;
}
si.deliveredTime = SystemClock.uptimeMillis();
r.deliveredStarts.add(si);
si.deliveryCount++;
//...
//2
bumpServiceExecutingLocked(r, "start");
if (!oomAdjusted) {
oomAdjusted = true;
updateOomAdjLocked(r.app);
}
int flags = 0;
if (si.deliveryCount > 0) {
flags |= Service.START_FLAG_RETRY;
}
if (si.doneExecutingCount > 0) {
flags |= Service.START_FLAG_REDELIVERY;
}
//3
r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
} catch (RemoteException e) {
//....
break;
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception", e);
break;
}
}
}
还是对几个关键点说明如下:
- 取出上一步放入pendingStarts中的StartItem对象,并记录了deliveredTime和deliveredCount。
- 调用bumpServiceExecutingLocked(r, "start"),套路与上面分析的一致。
- 调用客户端的scheduleServiceArgs。
我们重点看看scheduleServiceArgs在客户端侧的实现,一路跟踪下去,最后调用了方法handleServiceArgs方法,代码如下:
private final void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
//1 调用onStartCommand
int res = s.onStartCommand(data.args, data.flags, data.startId);
QueuedWork.waitToFinish();
try {
//2 调用serviceDoneExecuting
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 1, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
ensureJitEnabled();
} catch (Exception e) {
//...
}
}
}
首先是调用onStartCommand方法,然后其返回值作为参数调用AMS侧的方法serviceDoneExecuting(data.token, 1, data.startId, res),注意第二个参数为1,返回值为最后一个参数,我们再次看看serviceDoneExecuting的相关实现,代码如下:
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
//...
ServiceRecord r = (ServiceRecord)token;
boolean inStopping = mStoppingServices.contains(token);
if (r != null) {
if (r != token) {
//...
return;
}
//1 这次会进入这个分支了
if (type == 1) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
switch (res) {
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
// Don't stop if killed.
r.stopIfKilled = false;
break;
}
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
if (r.lastStartId == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
}
break;
}
case Service.START_REDELIVER_INTENT: {
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
si.deliveryCount = 0;
si.doneExecutingCount++;
// Don't stop if killed.
r.stopIfKilled = true;
}
break;
}
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
}
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
}
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
} else {
//...
}
}
}
这次type参数为1,所以会用到onStartCommond的返回值res了,我们知道onStartCommond的返回值有START_STICKY_COMPATIBILITY、START_STICKY、START_NOT_STICKY、START_REDELIVER_INTENT。对这些参数的处理就在AMS侧的这个方法中。
- START_STICKY:粘性的”。服务被异常kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
- START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
- START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
- START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
所以我们可以根据业务需求,在重写onStartCommond方法的时候返回不同的值。
startService的启动流程终于说完了;当然还有很多地方没有提到,但是我们可以这个流程为线索,一步步探索AMS中相关的实现,不至于密室在代码的海洋中,让自己对AMS有更深入的理解。
最后恭喜你已学习完了startService的启动流程,还有疑问或者需要从新再次学习该启动流程,可以从这里进入:
- Android中Service在新进程中的启动流程-CSDN博客
- Android中Service在新进程中的启动流程2-CSDN博客