当前位置: 移动技术网 > IT编程>移动开发>Android > Android Service的启动过程分析

Android Service的启动过程分析

2019年07月24日  | 移动技术网IT编程  | 我要评论

活取熊胆,黄泉逆旅,悄无声息的近义词

android service的启动过程分析

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

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

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

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

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

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

service启动过程

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

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

@override
public componentname startservice(intent service) {
 warnifcallingfromsystemprocess();
 return startservicecommon(service, muser);
}

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

private componentname startservicecommon(intent service, userhandle user) {
 try {
  validateserviceintent(service);
  service.preparetoleaveprocess();
  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);
 }
}

可以看到调用了activitymanagernative.getdefault()的startservice方法来启动service,activitymanagernative.getdefault()是activitymanagerservice,简称ams。

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

@override
public componentname startservice(iapplicationthread caller, intent service,
  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();
  componentname res = mservices.startservicelocked(caller, service,
    resolvedtype, callingpid, callinguid, callingpackage, userid);
  binder.restorecallingidentity(origid);
  return res;
 }
}

在上述的代码中,调用了activeservices的startservicelocked方法,那么现在service的启动过程从ams转移到了activeservices了。

继续跟进activeservices的startservicelocked方法,如下:

componentname startservicelocked(iapplicationthread caller, intent service, string resolvedtype,
  int callingpid, int callinguid, string callingpackage, int userid)
  throws transactiontoolargeexception {

 //代码省略

 servicelookupresult res =
  retrieveservicelocked(service, resolvedtype, callingpackage,
    callingpid, callinguid, userid, true, callerfg);

 //代码省略


 servicerecord r = res.record;

 //代码省略

 return startserviceinnerlocked(smap, service, r, callerfg, addtostarting);
}

在startservicelocked方法中又会调用startserviceinnerlocked方法,

我们瞧瞧startserviceinnerlocked方法,

componentname startserviceinnerlocked(servicemap smap, intent service, servicerecord r,
  boolean callerfg, boolean addtostarting) throws transactiontoolargeexception {
 processstats.servicestate stracker = 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,
  processrecord app, 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(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);
}

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

public void handlemessage(message msg) {

  //代码省略

  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(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) {
   // 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.classloader cl = packageinfo.getclassloader();
service = (service) cl.loadclass(data.info.name).newinstance();

接着调用contextimpl的createappcontext方法创建了一个contextimpl对象。

之后再调用loadedapk的makeapplication方法来创建application,这个创建过程如下:

public application makeapplication(boolean forcedefaultappclass,
  instrumentation instrumentation) {
 if (mapplication != null) {
  return mapplication;
 }

 application app = null;

 string appclass = mapplicationinfo.classname;
 if (forcedefaultappclass || (appclass == null)) {
  appclass = "android.app.application";
 }

 try {
  java.lang.classloader cl = getclassloader();
  if (!mpackagename.equals("android")) {
   initializejavacontextclassloader();
  }
  contextimpl appcontext = 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.
 sparsearray<string> packageidentifiers = 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(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);
}

public void handlemessage(message msg) {

  //代码省略

  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(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) {
    // 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呀。会有那种感觉,就是这里没错了。当然如果陷入胡同了也要兜出来。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网