内容简介:本文针对Service启动过程造成的ANR进行分析,同时启动过程限制在以下两个条件中:(A进程,调用startService()方法的进程;B进程,需要开启的Service所在的进程,A和B不是同一个进程)为了分析ANR所产生的原因,对于在不同进程中启动Service的流程需要有一个简单的了解,下面首先简要分析一个这个过程。
本文针对Service启动过程造成的ANR进行分析,同时启动过程限制在以下两个条件中:
(A进程,调用startService()方法的进程;B进程,需要开启的Service所在的进程,A和B不是同一个进程)
- 调用startService()启动service,暂不分析bindService()的情况
- 启动Service前,B进程不存在
为了分析ANR所产生的原因,对于在不同进程中启动Service的流程需要有一个简单的了解,下面首先简要分析一个这个过程。
2 Service启动流程
2.1 启动流程时序图
首先来看下简化的启动流程时序图,共分为两张图。第一张图是描述startService()的调用过程,第二张图是描述startProcess的调用过程。
图1:
图2:
2.2 流程分析
2.2.1 startService流程分析
从图1中我们已经可以看到一个大致的流程,其中有一个对ActiveServices.bringUpServiceLocked()的调用,这个方法对分析启动流程比较关键,下面我们对这个方法进行一下分析。
private final String bringUpServiceLocked(...) throws TransactionTooLargeException { ... // 1. 如果Service已经启动了,向B进程主线程发送消息,异步调用onStartCommand()方法 if (r.app != null && r.app.thread != null) { sendServiceArgsLocked(...); } ... // 2. 如果B进程存在,那么向B进程发送消息,启动Service app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); if (app != null && app.thread != null) { realStartServiceLocked(...); } ... // 3. 如果B进程不存在,那么首先创建进程,并将要启动的Service添加到mPendingServices中。添加到mPendingServices中的Service,在进程创建成功后,会被依次启动。 if (app == null) { if ((app=mAm.startProcessLocked(...) == null) { ... } ... } if (!mPendingServices.contains(r)) { mPendingServices.add(r); } ... } 复制代码
因为本文分析的是在另外一个未启动的进程中启动Service的情况,所以我们只需关注注释3的流程。这个流程会先去异步的启动B进程,同时将需要启动的Service加到mPendingServices中。接下来我们再去看看B进程启动的流程。
2.2.2 startProcess流程分析
进程在启动起来之后,会调用ActvityThread的main方法。这个方法主要做了两件事:
- 告诉AMS进程已启动:调用attach()方法
- 启动Looper
2.2.2.1 ActivityThread.attach()方法解析
attach()方法在ActivityThread一侧,最主要的是调用了AMS的attachApplication()方法,将自身进程的ApplicationThread对象发送给AMS。然后在AMS一侧,AMS则做了以下操作:
- 保存ApplicationThread对象
- 通过ApplicationThread对象调用ActivityThread的bindApplication()方法,bindApplication()方法就是在应用进程侧发送了一个BIND_APPLICATION消息然后就返回了,所以说bindApplication()方法是一个异步的方法。注意,此时应用进程中的looper并没有开始循环,所以这条message也仅是被增加到了消息队列中。
- 如果有待启动的Activity,给ActivityThread发送 CREATE_ACTIVITY 的消息,这个也是异步的。此时对于ActivityThread来说,主线程消息队列中有两条待处理的消息:1.bindApplication 2.createActivity。特别注意,此时主线程的Looper并没有开始循环,所以在后面开始循环的时候,第一条消息一定会延后第二条消息的执行。另外,这里只会发送一条 CREATE_ACTIVITY 的消息,因为只有最顶部的第一个待开启的Activity会发送消息。
- 如果有待启动的Service,给ActivityThread发送 CREATE_SERVICE 的消息,这个也是异步的。此时对于ActivityThread来说,主线程消息队列中有大于等于3条待处理的消息:1.bindApplication 2.createActivity 3.createService(一条或多条,不像Activity,Service会为所有等待启动的Service发送消息到消息队列)。注意,此时主线程的Looper同样没有开始循环,所以在后面开始循环的时候,前面几条消息的执行,会延后这些Service的启动。这样问题就来了,service executing timeout类型的ANR,其在AMS中的超时倒计时消息是在发送 CREATE_SERVICE 消息的同时发送到AMS的消息队列中的,即倒计时已经开始了,所以ActivityThread中排在Service启动前的消息,其处理时间都会被计算在Service的启动时间中,所以Service启动的anr,并不一定是自己的启动过程发生了耗时操作,也有可能是application初始化有耗时或先启动的Activity耗时造成的。
- 如果有待启动的BroadcastReceiver,给ActivityThread发送 RECEIVER 的消息。逻辑与前面一样,插到消息队列等待执行。
特别特别注意一点,就是attach()方法执行完毕之前,主线程的Looper一直是没有开始循环的状态。attach()执行完毕后,才会调用Looper.loop()开始消息循环。
当Looper开始循环之后,就会马上开始处理消息队列中的message。对于启动Service来说,那么此时队列中的message包括两条:1.BIND_APPLICATION 2.CREATE_SERVICE。
下面我们看下这两条消息都做了什么,BIND_APPLICATION。
BIND_APPLICATION 在ActivityThread里对应的就是handleBindApplication()方法。
private void handleBindApplication(AppBindData data) { ... Application app = data.info.makeApplication(...); ... } 复制代码
从上面我们看到有一个我们经常接触的操作——Application的初始化,让我们继续看看makeApplication做了什么? (data.info 是一个LoadedApk对象)
public Application makeApplication(...){ ... app = mActivityThread.mInstrumentation.newApplication(...); ... instrumentation.callApplicationOnCreate(app); ... } 复制代码
上面就是通过反射创建Application对象,然后调用application.onCreate()方法。这说明启动Service的过程中涉及到application的初始化。
接下来,我们看看第二条message做了什么?CREATE_SERVICE 在ActivityThread里对应的就是handleCreateService()方法
private void handleCreateService(...) { ... service = (Service) cl.loadClass(data.info.name).newInstance(); ... service.onCreate(); ... ActivityManagerNative.getDefault().serviceDoneExecuting(...); ... } 复制代码
这个方法做了三件事,反射创建Service对象,调用Service的onCreate(),通知AMS服务启动完成。
上面就是Service启动流程的大致介绍,有了对流程的简单了解,还不足以分析产生ANR的原因。我们还要再了解一下ANR的触发机制。
2.3 ANR触发机制
这个机制的本质就是AMS在某个时间节点发送一个“超时等待”的message到自己的消息队列中,如果超时时间结束前,service启动了,就移除这个message,如果没启动,这个message就会被执行。这个message执行的内容最终就是调用AMS的appNotResponding()方法触发ANR操作。如果大家有意愿更深入的了解ANR的触发原理,可以阅读一下GitYuan的博文 理解Android ANR的触发原理 。
那么对于Service来说,是在什么节点发送的“超时等待”的message,又是在什么时候移除的message呢?
回顾一下2.1节中的图2,以及2.2.2.1节中对attach()方法分析的第4个步骤,AMS是在进程attach,发送创建Service消息的时候,一并将“超时等待”消息发出去的。具体的方法可以查看该步骤调用的方法(ActiveServices.realStartServiceLocked()->bumpServiceExecutingLocked())。然后在ActivityThread.handleCreateService()方法中,通过ActivityManagerNative.getDefault().serviceDoneExecuting(...)方法告诉AMS服务启动完成,此时AMS会移除“超时等待”消息。
2.4 可能造成ANR的原因
在完成对service启动流程和ANR触发原理的简单介绍之后,我们来最终分析一下可能造成ANR的原因。
从上面的分析我们可以看到,新的进程在Looper开始循环前,消息队列中有两个消息等待处理(BIND_APPLICATION,CREATE_SERVICE),并且此时AMS侧已经开始对Service启动过程进行倒计时。所以对于Service来说,其启动时间会受到两个方法的影响:1.Application.onCreate() 2.Service.onCreate()。也就是说两个方法中任何一个方法有耗时操作,都有可能造成ANR。
以上所述就是小编给大家介绍的《Application.onCreate()会造成Service启动ANR么?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Class.forName 造成的线程阻塞
- 物联网黑客造成的经济损失有多少?
- 是什么造成了数据库的卡顿
- 一次断电造成ingress"问题" 原 荐
- 是什么造成了数据库的卡顿
- 再谈spring的循环依赖是怎么造成的?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。