Android Service启动过程完整分析
刚开始学习service的时候以为它是一个线程的封装,也可以执行耗时操作。其实不然,service是运行在主线程的。直接执行耗时操作是会阻塞主线程的。长时间就直接anr了。
我们知道service可以执行一些后台任务,是后台任务不是耗时的任务,后台和耗时是有区别的喔。
这样就很容易想到音乐播放器,天气预报这些应用是要用到service的。当然如果要在service中执行耗时操作的话,开个线程就可以了。
关于service的运行状态有两种,启动状态和绑定状态,两种状态可以一起。
启动一个service只需调用context的startservice方法,传进一个intent即可。看起来好像很简单的说,那是因为android为了方便开发者,做了很大程度的封装。那么你真的有去学习过service是怎么启动的吗?service的oncreate方法回调前都做了哪些准备工作?
先上一张图大致了解下,灰色背景框起来的是同一个类中的方法,如下图:
那接下来就从源码的角度来分析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呀。会有那种感觉,就是这里没错了。当然如果陷入胡同了也要兜出来。
这样的分析也能够摸清整体的过程,对于细节,等我有扎实的功底了在去研究吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: LeetCode-1. 两数之和