Android6.0之App的Service组件运行机制之StartService

现在来分析service是如何启动的。

service运行的进程

一般在AndroidManifest.xml配置service组件信息时,都会设置android:process属性,其值的格式“:XXX”,如下所示:


这样的话,AMS在启动这个service的时候,会为其创建一个名为"包名:XXX"的进程,在进程中运行这个service。

如果不设置android:process属性,那么service就可能会和其他组件,例如activity运行在一个进程中的UI线程,也就是主线程中。那么在这种情况下,service中执行耗时的操作是比较危险的。最后会解释原因。

service的启动方式:

  1. startService(),启动service之后,启动者和service之间的关系比较松散,启动者仅能停止service,不能和service之间交互。而且启动者,比如activity被销毁后,service不受影响,还可以继续运行。

  2. 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的过程大体上是:

  1. 首先检查AMS是否已经存在该service对应的ServiceRecord,存在的话,说明该service已经启动了;

  2. 如果service还没启动,那么从PMS中检查该service是否已被安装,也就是说来自某个安装的apk;

  3. 如果servic要求寄宿在的进程还没有被创建,那么就要创建一个符合要求的进程;

  4. 在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,过程如下:

Android6.0之App的Service组件运行机制之StartService_第1张图片
android_app_service-3.png

由上图可知,是通过getServiceMap()拿到一个ServiceMap,然后从中检查是否存在该service的ServiceRecord.

Android 系统是支持多用户的,所以系统把一个用户下面运行的service,都方到了ServiceMap结构中,可以通过用户ID拿到这个结构:

 class ServiceMap extends Handler {
   final int mUserId;
   final ArrayMap mServicesByName
           = new ArrayMap();
   final ArrayMap mServicesByIntent
           = new ArrayMap();

   final ArrayList mDelayedStartList
           = new ArrayList();
  final ArrayList mStartingBackground
           = new ArrayList();
  .............
 }

其中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类型的数组。

继续分析startServiceLocked(),略过启动service的发起者不处于前台的情况,那么:

 return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

这个方法中又是通过bringUpServiceLocked()来启动的service的,此时应该可以分为如下几种情况:

  1. service已经启动了,也就是说前面找到的ServiceRecord是从ServiceMap中获得,并没有创建ServiceRecord。而且已经与ProcessRecord关联,即r.app不为null。这种情况下就很简单了,调用 sendServiceArgsLocked()就可以了,这最终会导致app中service.onStartCommand()方法被执行。

  2. 如果service要求寄宿的进程还没被创建,那么就要通过AMS.startProcessLocked()创建一个进程

  3. 如果service要求寄宿的进程已经存在,而且也没有和前面找到的service的ServiceRecord关联那么调用realStartServiceLocked(),这最终会依次调用到app中service.onCreate()和onStartCommand()这两个service的生命周期方法。

先看第一种情况:

Android6.0之App的Service组件运行机制之StartService_第2张图片
android_app_service-4.png

当要启动的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要求寄宿的进程已经创建好了,过程如下所示:


Android6.0之App的Service组件运行机制之StartService_第3张图片
android_app_service-5.png

在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的过程有了大体上的了解了。

你可能感兴趣的:(Android6.0之App的Service组件运行机制之StartService)