美文网首页Android技术知识Android进阶之路Android知识
Android6.0之App的Service组件运行机制之Sta

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

作者: 小爨 | 来源:发表于2016-08-26 16:33 被阅读0次

    现在来分析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的启动方式:

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

    android_app_service-3.png

    由上图可知,是通过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的,此时应该可以分为如下几种情况:

    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的生命周期方法。

    先看第一种情况:

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


    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组件运行机制之Sta

        本文链接:https://www.haomeiwen.com/subject/ejxzsttx.html