为什么80%的码农都做不了架构师?>>>   

刚开始学习Android Service的时候以为它是一个线程的封装,也可以执行耗时操作。其实不然,Service是运行在主线程的。直接执行耗时操作是会阻塞主线程的。长时间就直接ANR了。

  我们知道Service可以执行一些后台任务,是后台任务不是耗时的任务,后台和耗时是有区别的喔。

  这样就很容易想到音乐播放器,天气预报这些应用是要用到Service的。当然如果要在Service中执行耗时操作的话,开个线程就可以了。

  关于Service的运行状态有两种,启动状态和绑定状态,两种状态可以一起。

  启动一个Service只需调用Context的startService方法,传进一个Intent即可。看起来好像很简单的说,那是因为Android为了方便开发者,做了很大程度的封装。那么你真的有去学习过Service是怎么启动的吗?Service的onCreate方法回调前都做了哪些准备工作?

  先上一张图大致了解下,灰色背景框起来的是同一个类中的方法,如下图:

那接下来就从源码的角度来分析Service的启动过程。

  当然是从Context的startService方法开始,Context的实现类是ContextImpl,那么我们就看到ContextImpl的startService方法即可,如下:

  @Overridepublic ComponentNamestartService(Intentservice) {

  warnIfCallingFromSystemProcess();

  return startServiceCommon(service, mUser);

  }

  会转到startServiceCommon方法,那跟进startServiceCommon方法方法瞧瞧。

  private ComponentNamestartServiceCommon(Intentservice, UserHandleuser) {

  try {

  validateServiceIntent(service);

  service.prepareToLeaveProcess();

  ComponentNamecn = ActivityManagerNative.getDefault().startService(

  mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(

  getContentResolver()), getOpPackageName(), user.getIdentifier());

  //代码省略

  return cn;

  } catch (RemoteException e) {

  throw new RuntimeException("Failure from system", e);

  }

  }

  可以看到调用了ActivityManagerNative.getDefault()的startService方法来启动Service,ActivityManagerNative.getDefault()是ActivityManagerService,简称AMS。

  那么现在启动Service的过程就转移到了ActivityManagerService,我们关注ActivityManagerService的startService方法即可,如下:

  @Overridepublic ComponentNamestartService(IApplicationThreadcaller, Intentservice,

  String resolvedType, String callingPackage, int userId)

  throws TransactionTooLargeException {

  //代码省略

  synchronized(this) {

  final int callingPid = Binder.getCallingPid();

  final int callingUid = Binder.getCallingUid();

  final long origId = Binder.clearCallingIdentity();

  ComponentNameres = mServices.startServiceLocked(caller, service,

  resolvedType, callingPid, callingUid, callingPackage, userId);

  Binder.restoreCallingIdentity(origId);

  return res;

  }

  }

  在上述的代码中,调用了ActiveServices的startServiceLocked方法,那么现在Service的启动过程从AMS转移到了ActiveServices了。

  继续跟进ActiveServices的startServiceLocked方法,如下:

  ComponentNamestartServiceLocked(IApplicationThreadcaller, Intentservice, String resolvedType,

  int callingPid, int callingUid, String callingPackage, int userId)

  throws TransactionTooLargeException {

  //代码省略

  ServiceLookupResultres =

  retrieveServiceLocked(service, resolvedType, callingPackage,

  callingPid, callingUid, userId, true, callerFg);

  //代码省略

  ServiceRecord r = res.record;

  //代码省略

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

  }

  在startServiceLocked方法中又会调用startServiceInnerLocked方法,

  我们瞧瞧startServiceInnerLocked方法,

  ComponentNamestartServiceInnerLocked(ServiceMapsmap, Intentservice, ServiceRecord r,

  boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {

  ProcessStats.ServiceStatestracker = r.getTracker();

  if (stracker != null) {

  stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);

  }

  r.callStart = false;

  synchronized (r.stats.getBatteryStats()) {

  r.stats.startRunningLocked();

  }

  String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);

  //代码省略

  return r.name;

  }

  startServiceInnerLocked方法内部调用了bringUpServiceLocked方法,此时启动过程已经快要离开ActiveServices了。继续看到bringUpServiceLocked方法。如下:

  private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,

  boolean whileRestarting) throws TransactionTooLargeException {

  //代码省略

  if (app != null && app.thread != null) {

  try {

  app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);

  realStartServiceLocked(r, app, execInFg);

  return null;

  }

  //代码省略

  return null;

  }

  省略了大部分if判断,相信眼尖的你一定发现了核心的方法,那就是

  realStartServiceLocked,没错,看名字就像是真正启动Service。那么事不宜迟跟进去探探吧。如下:

  private final void realStartServiceLocked(ServiceRecord r,

  ProcessRecordapp, boolean execInFg) throws RemoteException {

  //代码省略

  boolean created = false;

  try {

  //代码省略

  app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);

  app.thread.scheduleCreateService(r, r.serviceInfo,

  mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),

  app.repProcState);

  r.postNotification();

  created = true;

  } catch (DeadObjectException e) {

  Slog.w(TAG, "Application dead when creating service " + r);

  mAm.appDiedLocked(app);

  throw e;

  }

  //代码省略

  sendServiceArgsLocked(r, execInFg, true);

  //代码省略

  }

  找到了。app.thread调用了scheduleCreateService来启动Service,而app.thread是一个ApplicationThread,也是ActivityThread的内部类。此时已经到了主线程。

  那么我们探探ApplicationThread的scheduleCreateService方法。如下:

  public final void scheduleCreateService(IBindertoken,

  ServiceInfoinfo, CompatibilityInfocompatInfo, int processState) {

  updateProcessState(processState, false);

  CreateServiceData s = new CreateServiceData();

  s.token = token;

  s.info = info;

  s.compatInfo = compatInfo;

  sendMessage(H.CREATE_SERVICE, s);

  }

  对待启动的Service组件信息进行包装,然后发送了一个消息。我们关注这个CREATE_SERVICE消息即可。

  public void handleMessage(Messagemsg) {

  //代码省略

  case CREATE_SERVICE:

  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");

  handleCreateService((CreateServiceData)msg.obj);

  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

  break;

  //代码省略

  }

  在handleMessage方法中接收到这个消息,然后调用了handleCreateService方法,跟进handleCreateService探探究竟:

  private void handleCreateService(CreateServiceDatadata) {

  // If we are getting ready to gc after going to the background, well

  // we are back active so skip it.

  unscheduleGcIdler();

  LoadedApkpackageInfo = getPackageInfoNoCheck(

  data.info.applicationInfo, data.compatInfo);

  Serviceservice = null;

  try {

  java.lang.ClassLoadercl = 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);

  ContextImplcontext = ContextImpl.createAppContext(this, packageInfo);

  context.setOuterContext(service);

  Applicationapp = packageInfo.makeApplication(false, mInstrumentation);

  service.attach(context, thisdata.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) {

  // nothing to do.

  }

  } catch (Exception e) {

  if (!mInstrumentation.onException(service, e)) {

  throw new RuntimeException(

  "Unable to create service " + data.info.name

  + ": " + e.toString(), e);

  }

  }

  }

  终于击破,这个方法很核心的。一点点分析

  首先获取到一个LoadedApk对象,在通过这个LoadedApk对象获取到一个类加载器,通过这个类加载器来创建Service。如下:

  java.lang.ClassLoadercl = packageInfo.getClassLoader();service = (Service) cl.loadClass(data.info.name).newInstance();

  接着调用ContextImpl的createAppContext方法创建了一个ContextImpl对象。

  之后再调用LoadedApk的makeApplication方法来创建Application,这个创建过程如下:

  public ApplicationmakeApplication(boolean forceDefaultAppClass,

  Instrumentationinstrumentation) {

  if (mApplication != null) {

  return mApplication;

  }

  Applicationapp = null;

  String appClass = mApplicationInfo.className;

  if (forceDefaultAppClass || (appClass == null)) {

  appClass = "android.app.Application";

  }

  try {

  java.lang.ClassLoadercl = getClassLoader();

  if (!mPackageName.equals("android")) {

  initializeJavaContextClassLoader();

  }

  ContextImplappContext = ContextImpl.createAppContext(mActivityThread, this);

  app = mActivityThread.mInstrumentation.newApplication(

  cl, appClass, appContext);

  appContext.setOuterContext(app);

  } catch (Exception e) {

  if (!mActivityThread.mInstrumentation.onException(app, e)) {

  throw new RuntimeException(

  "Unable to instantiate application " + appClass

  + ": " + e.toString(), e);

  }

  }

  mActivityThread.mAllApplications.add(app);

  mApplication = app;

  if (instrumentation != null) {

  try {

  instrumentation.callApplicationOnCreate(app);

  } catch (Exception e) {

  if (!instrumentation.onException(app, e)) {

  throw new RuntimeException(

  "Unable to create application " + app.getClass().getName()

  + ": " + e.toString(), e);

  }

  }

  }

  // Rewrite the R 'constants' for all library apks.

  SparseArraypackageIdentifiers = getAssets(mActivityThread)

  .getAssignedPackageIdentifiers();

  final int N = packageIdentifiers.size();

  for (int i = 0; i < N; i++) {

  final int id = packageIdentifiers.keyAt(i);

  if (id == 0x01 || id == 0x7f) {

  continue;

  }

  rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);

  }

  return app;

  }

  当然Application是只有一个的,从上述代码中也可以看出。

  在回来继续看handleCreateService方法,之后service调用了attach方法关联了ContextImpl和Application等

  最后service回调了onCreate方法,

  service.onCreate();mServices.put(data.token, service);

  并将这个service添加进了一个了列表进行管理。

  至此service启动了起来,以上就是service的启动过程。

  你可能还想要知道onStartCommand方法是怎么被回调的?可能细心的你发现了在ActiveServices的realStartServiceLocked方法中,那里还有一个sendServiceArgsLocked方法。是的,那个就是入口。

  那么我们跟进sendServiceArgsLocked方法看看onStartCommand方法是怎么回调的。

  private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,

  boolean oomAdjusted) throws TransactionTooLargeException {

  final int N = r.pendingStarts.size();

  //代码省略

  try {

  //代码省略

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

  } catch (TransactionTooLargeException e) {

  if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="

  + si.intent);

  caughtException = e;

  } catch (RemoteException e) {

  // Remote process gone... we'll let the normal cleanup take care of this.

  if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);

  caughtException = e;

  }

  //代码省略

  }

  可以看到onStartCommand方法回调过程和onCreate方法的是很相似的,都会转到app.thread。那么现在就跟进ApplicationThread的scheduleServiceArgs。

  你也可能猜到了应该又是封装一些Service的信息,然后发送一个消息, handleMessage接收。是的,源码如下:

  public final void scheduleServiceArgs(IBindertoken, boolean taskRemoved, int startId,

  int flags ,Intentargs) {

  ServiceArgsData s = new ServiceArgsData();

  s.token = token;

  s.taskRemoved = taskRemoved;

  s.startId = startId;

  s.flags = flags;

  s.args = args;

  sendMessage(H.SERVICE_ARGS, s);

  }

  public void handleMessage(Messagemsg) {

  //代码省略

  case SERVICE_ARGS:

  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");

  handleServiceArgs((ServiceArgsData)msg.obj);

  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

  break;

  //代码省略

  }

  咦,真的是这样。谜底应该就在handleServiceArgs方法了,那么赶紧瞧瞧,源码如下:

  private void handleServiceArgs(ServiceArgsDatadata) {

  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) {

  // nothing to do.

  }

  ensureJitEnabled();

  } catch (Exception e) {

  if (!mInstrumentation.onException(s, e)) {

  throw new RuntimeException(

  "Unable to start service " + s

  + " with " + data.args + ": " + e.toString(), e);

  }

  }

  }

  }

  可以看到回调了onStartCommand方法。

  以上就是Service的启动过程的源码分析。

  从中,我理解了Service的启动过程的同时,阅读源码的能力也提高了,分析源码的时候我没能力把每一个变量,每一个方法都搞懂,我关注的都是一些关键的字眼,比如这篇文章就是start呀,service呀。会有那种感觉,就是这里没错了。当然如果陷入胡同了也要兜出来。

文章来源:伯乐在线

转载于:https://my.oschina.net/sdeqq/blog/786605

Android Service是如何启动的?相关推荐

  1. Android service的两种使用方式bindService和startService使用方式

    服务与多线程的区别: 服务仍然是运行在主线程中,而Thread则是真正的开启了一个新的线程来执行先关操作. 那为什么要用服务呢,初步理解是服务是为了更好地管理线程.试想,当我们在某一个activity ...

  2. android init.rc中启动的service 默认是disable的,后续如何启动此服务

    如果 android init.rc中启动的service 默认是disable的,如何才能启动此服务呢? init.rc中可以直接启动service 附带的参数决定启动程序的状态,例如数据业务中配置 ...

  3. android服务的启动过程,Android Service的启动过程(上)

    原标题:Android Service的启动过程(上) (点击上方公众号,可快速关注) 来源:伯乐在线专栏作者 - xuyinhuan 链接:http://android.jobbole.com/85 ...

  4. android服务无法启动,Android服务无法启动(Android service would't start)

    Android服务无法启动(Android service would't start) 我正在尝试在Android中实现简单的服务,但我无法统计基本服务. 这是我的主要课程: import java ...

  5. Android Service的启动过程

    刚开始学习Service的时候以为它是一个线程的封装,也可以执行耗时操作.其实不然,Service是运行在主线程的.直接执行耗时操作是会阻塞主线程的.长时间就直接ANR了. 我们知道Service可以 ...

  6. 一篇文章看明白 Android Service 启动过程

    Android - Service 启动过程 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 一篇文章看明白 An ...

  7. Android service启动流程分析.

    文章仅仅用于个人的学习记录,基本上内容都是网上各个大神的杰作,此处摘录过来以自己的理解学习方式记录一下. 参考链接: https://my.oschina.net/youranhongcha/blog ...

  8. Android service 启动篇之 startService

    系列博文: Android 中service 详解 Android service 启动篇之 startService Android service 启动篇之 bindService Android ...

  9. android 浏览器源码分析,从源码出发深入理解 Android Service

    原标题:从源码出发深入理解 Android Service 原文链接: 建议在浏览器上打开,删除了大量代码细节,:) 本文是 Android 系统学习系列文章中的第三章节的内容,介绍了 Android ...

最新文章

  1. tomcat8.5 远程访问 manager页面
  2. AI公开课:18.05.16 周明博士(MSRA副院长)—北大AI第十一讲之《语言智能的进展》课堂笔记——你了解语言智能
  3. 深入理解HashMap和LinkedHashMap的区别
  4. python中module的意思_python中module的意思
  5. 宏内核linux,Linux 为什么还要坚持使用宏内核?
  6. leetcode-551-Student Attendance Record I(判断是否出现连续几个相同字符)
  7. HTML+CSS+JS实现 ❤️酷炫的时光隧道旅行动特效❤️
  8. IBM语音识别能力逼近人类水平,获深度学习巨头Yoshua Bengio盛赞
  9. 比赛 | 第一届古汉语分词与词性标注国际评测来啦
  10. 高兴与愉快的心情,难以言语。
  11. HTML罗盘时钟代码
  12. 用JSDoc生成js文档
  13. android 关闭第三方应用,Android禁用第三方应用
  14. 职高计算机班主任工作计划,教学工作计划:高职班主任工作计划
  15. vite-plugin-eslint缓存导致eslint一直报错的问题
  16. orangepi pc GPIO 管脚图
  17. C/C++ 内使用贝塞尔函数
  18. 爬虫实战2(下):爬取豆瓣影评
  19. 如何安装 zlib-dev
  20. 退出mysql控制台与退出mysql

热门文章

  1. android aar项目_Android-我所认知的VLayout
  2. oracle交流 提问,Oracle常见提问6(转)
  3. java xml 画表格_用js+xml自动生成表格的东西
  4. matlab 符号表,MATLAB——matlab特殊符号表【转载】
  5. os x php,OS X 安装php7.0
  6. 微型计算机原理与接口技术 王建国,微型计算机原理与接口技术课后习题答案及部分定义总结(王建国主编)...
  7. linux外接NetApp存储,netapp linux iscsi 实现
  8. gitlab root
  9. C++语言函数重载详解和示例
  10. linux 安装nodejs8,CentOS 安装NodeJS V8.0.0的方法