【Android源码】Service的启动过程

通常情况下,我们启动或者绑定一个Service是通过如下代码:

Intent intent = new Intent(this, TestService.class);
startService(intent);
bindService(intent, mServiceConneftion, BIND_AUTO_CREATE);

Service的启动是从ContextWrapper的startService开始的:

@Override
public ComponentName startService(Intent service) {
   return mBase.startService(service);
}

上面代码中的mBase的类型是Context,而从Activity的启动流程我们知道了,ContextImpl对象就是Context的具体实现,Activity被创建的时候会通过attach方法将ContextImpl对象关联起来,而这个ContextImpl就是mBase。

// ContextImpl.java
@Override
public ComponentName startService(Intent service) {
   warnIfCallingFromSystemProcess();
   return startServiceCommon(service, mUser);
}

private ComponentName startServiceCommon(Intent service, UserHandle user) {
   try {
       validateServiceIntent(service);
       service.prepareToLeaveProcess(this);
       ComponentName cn = ActivityManagerNative.getDefault().startService(
           mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                       getContentResolver()), 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());
           }
       }
       return cn;
   } catch (RemoteException e) {
       throw e.rethrowFromSystemServer();
   }
}

startService又会调用startServiceCommon,而在startServiceCommon中,又会通过ActivityManagerNative.getDefault().startService来启动一个服务。
ActivityManagerNative其实就是AMS:

@Override
public ComponentName startService(IApplicationThread caller, Intent service,
       String resolvedType, 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);
   synchronized(this) {
       final int callingPid = Binder.getCallingPid();
       final int callingUid = Binder.getCallingUid();
       final long origId = Binder.clearCallingIdentity();
       ComponentName res = mServices.startServiceLocked(caller, service,
               resolvedType, callingPid, callingUid, callingPackage, userId);
       Binder.restoreCallingIdentity(origId);
       return res;
   }
}

其中通过mServices.startServiceLocked来完成后续的启动过程,mServicesActiveServices,是一个辅助AMS来管理Service的类,包括启动、绑定、停止等操作。

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
       int callingPid, int callingUid, String callingPackage, final int userId)
       throws TransactionTooLargeException {
    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
       boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
   String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
   return r.name;
}

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
       boolean whileRestarting, boolean permissionsReviewRequired)
       throws TransactionTooLargeException {
    realStartServiceLocked(r, app, execInFg);       
}

通过不断的调用最终调用到了realStartServiceLocked(r, app, execInFg)方法,通过名称我们可以猜测这个就是最终创建Service的方法:

app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);

通过app.threadscheduleCreateService来创建Service对象并调用onCreate。

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);
}

 case CREATE_SERVICE:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
     handleCreateService((CreateServiceData)msg.obj);
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;

这个和Activity的处理几乎一样,都是通过H这个Handler对象来发送消息,并处理的创建过程的:

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) 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 context = ContextImpl.createAppContext(this, packageInfo);
       context.setOuterContext(service);

       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);
       try {
           ActivityManagerNative.getDefault().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);
       }
   }
}

这个方法主要做了这样几件事:

  1. 通过类加载器创建Service实例。
  2. 创建Application对象,并通过service的attach方法关联Application。
  3. 调用service的onCreate方法将Service对象存到ActivityThread的管理Service的列表中。

我们在回到realStartServiceLocked中,在scheduleCreateService之后又调用了sendServiceArgsLocked(r, execInFg, true)方法。
scheduleCreateService方法是创建Service对象,并调用onCreate方法。
sendServiceArgsLocked方法则是调用了onStartCommand方法。

r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);

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);
}

 case SERVICE_ARGS:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
     handleServiceArgs((ServiceArgsData)msg.obj);
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
     
private void handleServiceArgs(ServiceArgsData data) {
   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);
           } else {
               s.onTaskRemoved(data.args);
               res = Service.START_TASK_REMOVED_COMPLETE;
           }

           QueuedWork.waitToFinish();

           try {
               ActivityManagerNative.getDefault().serviceDoneExecuting(
                       data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
           } catch (RemoteException e) {
               throw e.rethrowFromSystemServer();
           }
           ensureJitEnabled();
       } catch (Exception e) {
           if (!mInstrumentation.onException(s, e)) {
               throw new RuntimeException(
                       "Unable to start service " + s
                       + " with " + data.args + ": " + e.toString(), e);
           }
       }
   }
}

这个时候调用了res = s.onStartCommand(data.args, data.flags, data.startId)方法,也就是说Service的启动过程已经分析完了。

后篇: Service的绑定过程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容