现在来分析service是如何启动的。
service运行的进程
一般在AndroidManifest.xml配置service组件信息时,都会设置android:process属性,其值的格式“:XXX”,如下所示:
<service android:name="com.godin.demo.tmp.TmpService1"
android:process=":p1"></service>
这样的话,AMS在启动这个service的时候,会为其创建一个名为"包名:XXX"的进程,在进程中运行这个service。
如果不设置android:process属性,那么service就可能会和其他组件,例如activity运行在一个进程中的UI线程,也就是主线程中。那么在这种情况下,service中执行耗时的操作是比较危险的。最后会解释原因。
service的启动方式:
startService(),启动service之后,启动者和service之间的关系比较松散,启动者仅能停止service,不能和service之间交互。而且启动者,比如activity被销毁后,service不受影响,还可以继续运行。
bindService(),启动的service和启动者可以交互,启动者可以调用service的方法,即rpc。启动者销毁后,如果没有其他组件绑定该service,那么该service也被销毁。
两个方法都是异步的,也就是说当这两个方法返回的时候,service可能还没创启动好,还没执行相应的回调方法。
不管是哪种启动方式,终究还是由AMS来启动的,因为service毕竟是一个组件,而AMS是组件的管理者。
startService启动service
startService()方法实际上ContextImpl.startService():
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
}
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess();
// 通过AMS的代理对象,向AMS发起rpc,调用其startService
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
..........
return cn;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
通过以上代码可知,实际上调用AMS.startService()方法来启动service:
public ComponentName startService(
IApplicationThread caller, // 发起者的进程的ActivityThread.mApplication,即ApplicationThread binder对象的代理binder
Intent service,//启动service的intent
String resolvedType,// 没用
String callingPackage,// 发起者的包名
int userId)
throws TransactionTooLargeException {
...............
synchronized(this) {
..........
// mServices是ActiveService对象
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
启动service的过程大体上是:
首先检查AMS是否已经存在该service对应的ServiceRecord,存在的话,说明该service已经启动了;
如果service还没启动,那么从PMS中检查该service是否已被安装,也就是说来自某个安装的apk;
如果servic要求寄宿在的进程还没有被创建,那么就要创建一个符合要求的进程;
在service要求寄宿的进程中,创建service对象,并执行其生命周期方法;
接下来分析ActiveService.startServiceLocked:
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
final boolean callerFg;
// 检查调用者是否处于前台,是的话设置callerFg为true
if (caller != null) {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
.........
}
callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
} else {
callerFg = true;
}
// 检查AMS是否存在该service的ServiceRecord,没的话创建
ServiceLookupResult res =retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg);
.........
ServiceRecord r = res.record;
.....
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
retrieveServiceLocked()的作用就是检查和创建ServiceRecord,过程如下:
由上图可知,是通过getServiceMap()拿到一个ServiceMap,然后从中检查是否存在该service的ServiceRecord.
Android 系统是支持多用户的,所以系统把一个用户下面运行的service,都方到了ServiceMap结构中,可以通过用户ID拿到这个结构:
class ServiceMap extends Handler {
final int mUserId;
final ArrayMap<ComponentName, ServiceRecord> mServicesByName
= new ArrayMap<ComponentName, ServiceRecord>();
final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent
= new ArrayMap<Intent.FilterComparison, ServiceRecord>();
final ArrayList<ServiceRecord> mDelayedStartList
= new ArrayList<ServiceRecord>();
final ArrayList<ServiceRecord> mStartingBackground
= new ArrayList<ServiceRecord>();
.............
}
其中mServicesByName这个ArrayMap是以service的组件名为key的,而mServicesByIntent是以启动这个service的intent为key的。只要是运行着的service至少在这两者置一中进行了记录,所以可以通过这两个map快速查找service是否已经运行。运行的话,返回其ServiceRecord.
如果这两个map中都没有,那就创建一个ServiceRecord,并添加到这两个map中。
retrieveServiceLocked()返回的ServiceLookupResult类:
private final class ServiceLookupResult {
final ServiceRecord record;
final String permission;
ServiceLookupResult(ServiceRecord _record, String _permission) {
record = _record;
permission = _permission;
}
}
可以看到是对ServiceRecord的简单二次封装,其中permission是用来启动这个service时需要的权限。比如有的app中的service,在AndroidManifest.xml还配置了启动他的权限,那么这个权限就记录在这里。
那么继续分析startServiceLocked():
// 拿到前面找到的ServiceRecord
ServiceRecord r = res.record;
.....
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
这段代码的无非就是设置一下ServiceRecord的一些字段,注意这里没有设置ServiceRecord.app,也就是说如果这个ServiceRecord对象是新创建的,那么它还没有与进程关联,此时r.app为NULL.如果ServiceRecord已经存在,也就是service已经启动了,那么r.app就不会为null。
重点看r.pendingStarts.add操作。这个操作将启动service所需要的信息,如intent等都保存了起来,只要这个pendingStarts不为null,就说明有启动该service的请求还没处理。当后面处理完之后,会将存储的信息从r.pendingStarts移动到r.deliveredStarts中。这两个成员都是ArrayList<StartItem>类型的数组。
继续分析startServiceLocked(),略过启动service的发起者不处于前台的情况,那么:
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
这个方法中又是通过bringUpServiceLocked()来启动的service的,此时应该可以分为如下几种情况:
service已经启动了,也就是说前面找到的ServiceRecord是从ServiceMap中获得,并没有创建ServiceRecord。而且已经与ProcessRecord关联,即r.app不为null。这种情况下就很简单了,调用 sendServiceArgsLocked()就可以了,这最终会导致app中service.onStartCommand()方法被执行。
如果service要求寄宿的进程还没被创建,那么就要通过AMS.startProcessLocked()创建一个进程
如果service要求寄宿的进程已经存在,而且也没有和前面找到的service的ServiceRecord关联那么调用realStartServiceLocked(),这最终会依次调用到app中service.onCreate()和onStartCommand()这两个service的生命周期方法。
先看第一种情况:
当要启动的service已经启动的话,ServiceRecord.app.thread 就是这个service所在进程的ActivityThread.mAppThread 这个binder实体在AMS中的代理binder。那么就可以通过这个代理binder跨进程调用service所在进程的ActivityThread.mAppThread.scheduleServiceArgs()方法:
public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
int flags ,Intent args) {
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.taskRemoved = taskRemoved;
s.startId = startId;
s.flags = flags;
s.args = args;
sendMessage(H.SERVICE_ARGS, s);
}
传入的参数token,是该service在AMS中ServiceRecord 这个binder实体的代理binder;
taskRemoved传入的是fasle;
flags是启动service时的flag。
intent是启动这个service的intent。
通过这里的sendMessage()方法,就可以确定startService()是一个异步方法了。因为到这里就会返回了。接下来的消息处理是一个异步的过程,startService()不会等到消息处理完之后才返回。
private void handleServiceArgs(ServiceArgsData data) {
// 根据ServiceRecord代理binder,在ActivityThread.mServices中索引到对应的service
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);
...........
实际上后两种情况,可以归结为service还没启动这一大类中去。
暂时跳过创建App进程的情况,后面会单独分析App进程的创建过程。这里我们只要知道AMS会根据app的组件需求,例如某个组件设置了android:processName指定了另外一个进程名字,而这个进程有不存在,那么AMS会向zygote进程发出创建进程的请求,zygote创建进程之后,首先执行的代码是ActivityThread.main()方法即可。
假设service要求寄宿的进程已经创建好了,过程如下所示:
在realStartServiceLocked()方法中会跨进程调用service寄宿的进程的handleCreateService()方法。在该方法中先通过getPackageInfoNoCheck()得到要启动的service的代码所在的apk在进程中的代表:LoadedApk对象。这个对象中记录了加载该apk的classloader,然后利用loadclass,装载要启动的service的类,并通过newInstance()创建了一个service对象。
紧接着为service组件创建上下文context,然后通过makeApplication()方法拿到其所在进程的application对象,然后调用service.attch()和service.OnCreate()方法:
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
service.attach()将ServiceRecord在service进程中的代理binder保存在了service.mToken中,并且作为key保存在ActivityThread.mServices中,value是service/
service.attach()方法执行之后,才会执行service的第一个生命周期方法onCreate().
由前面的时序图可知,realStartServiceLocked()中先跨进程调用service所在进程的scheduleCreateService()方法,该方法发送了一个消息CREATE_SERVICE之后便会返回这是一个异步处理的过程。
然后调用realStartServiceLocked()又调用sendServiceArgsLocked()方法,该方法在发送一个SERVICE_ARGS消息。
这两个异步消息均通过service所在进程的handler发送, 都在service所在的主线程中的looper中被处理,而且线处理CREATE_SERVICE,后处理SERVICE_ARGS消息。也就是先调用service.onCreate(),后执行service.onStartCommand()方法。
但实际上service中最先执行的是service.attach()方法。
CREATE_SERVICE和SERVICE_ARGS 这两个消息都实在进程的主线程也就是ui线程中执行的,所以onCreate()和onStartCommand()有太耗时的操作时,要在开启一个线程来成执行。
到这里为止应该对startService()启动service的过程有了大体上的了解了。