对于近期4.4和5.x上出现服务无法绑定的问题分析如下:
【具体原因分析】
我们从用户侧拿到的servicedump信息:
从用户侧拿到的sevices文件,都有这几特征:
解释这几个值。
1.serviceRecord:就是用于服务对象本身
1.App=null:说明服务于进程没有关联起来
2.lastStartid=124;说明主进程调用startservice接近124次了。对这个频繁的调用,只有两种可能:
A. 进程被杀后服务自启,如果要触发这个条件,相当于进程短时间内一直被kill,从log看,是可以排除这种情况
B. 客户端判断绑定没成功进行重邦定,很显然,这个124值就是我们一直在调用
1. 至于这个restartDelay这个值:说明这个ServiceRecord是要延时启动的。
2.
从上面的这些信息。我们结合源码,看到两个可疑点:
无论是何种方式启动或重启都会调用这个方法:bringUpServiceLocked,这两处(标红的1,2)会直接return,这种分支执行的结果就是永远都无法绑定成功。
大家先记住这张图。后面分析会用到
首先我们app==null.所以不可能是第一种情况,接下来重点分析第二种情况。
首先,出现二种情况的条件是如下:
Ø whileRestarting=false;
Ø r.restartDelay>0 //上面我们抓到的dump可以看到restartDelay是大于0的。哪我们主要看这个值whileRestarting是什么情况了。因为这里失败时,是我们主动调用了startservice和bindservices。
查代码后,发现有三个地方会调用:
1. startServiceInnerLocked-->bringUpServiceLocked(r, service.getFlags(), callerFg, false);
2. bindServiceLocked-->bringUpServiceLocked(s,service.getFlags(), callerFg, false)
3. performServiceRestartLocked-->bringUpServiceLocked(r,r.intent.getIntent().getFlags(), r.createdFromFg, true);
我们调用start和bindservice都是传的false,而restartdelay这个值大于0.坑爷啊, 这样就是死循环,无论如何也是邦定不成功了(而performServiceRestartLocked这个方法只有系统重启service才会调用,非我们主动调用,所以无关)
Ps:Start和bind传进来的值我们无法改变,哪问题一定是出现这个restartDelay这个值上。这个值大于0:我们猜想是上次系统重启了服务,但启动的过程中失败了,而restartDelay这个值又没有重置,导致后面重启一直死循环接着看下面的分析:
看看重置代码如下:
而这个方法只有这个方法会调用:
从这里看,调用的条件满足任一要求即可:
1.此servicecRecord在重启服务列表里面
2.uid不相等
我们来看下调用这个方法都有哪些方法及传的参数:
1 startServiceLocked-->unscheduleServiceRestartLocked(r,callingUid, false)
2 bindServiceLocked -->unscheduleServiceRestartLocked(s,callerApp.info.uid, false)
3 bringDownServiceLocked--> unscheduleServiceRestartLocked(r, 0,true)
注:start和bind的调用都是用户主动触发的,能进入这个逻辑的只有一个条件:此服务已在重启列表里面。Ps: 只有系统重启的服务才会放到重启列表里
哪剩下我们看下这个bringDownServiceLocked-->unscheduleServiceRestartLocked(r, 0, true)他传的是0.,自然这个值肯定resetRestartCounter是会被调用的。
找了下,要执行bringDownServiceLocked这个方法:有如下方法:
1. 这5个方法中,我圈中的3个方法是和重启服务强相关的:
2. 第1个方法调用,意味着服务走完了完整的调用流程。
3. 第2个方法的引用方法有如下:
4.
5. 第5个方法正是服务被杀后走的第一个方法:
重点看这个方法:killServicesLocked,这个方法只要重启都会到到这个方法:
这个方法很长:做的事有三件:
1. 清除app的服务状态,相当于初始化一些变量:
2. 清除所有连接
3. 清理此app剩余的服务,因为有些是要重启scheduleServiceRestartLocked,有些是没必要的bringDownServiceLocked:
结合上面的分析:结合此处标注的这三处圈圈都是有可能对restartDelay重置的。
1. removeConnectionLockedàbringDownServiceIfNeededLockedàbringDownServiceLocked
2. 第二处和第三处均是:bringDownServiceLocked.
3. 以上几处均没用调用
按时间来执行,首先执行的是:removeConnectionLocked这个方法:
Remove这个方法会清除掉此进程的所有服务连接。并unbind的,但如果Un失败,就会调用
serviceProcessGoneLocked—》serviceDoneExecutingLocked(r, true, true)
一定会执行这个方法,此时会把此服务从r.app里面remove掉,这样此进程里面就再也没有这个服务了。此时如果是bind_auto_creat标记的服务和delay就会被重置:
回到killservicelock方法上来:此时,由于app里面已经没有这个serviceRecord了,但这个表里还是有的同,后面的遍历app.services是没用了。
因为我们的服务已被remove,所以这个服务永远也不会启动了 。
后面的bringDownServiceLocked.和scheduleServiceRestartLocked就无法执行了。所以这次启动服务也就失败了。如果此时我们检测到service没有起来:主动start/bind。会有以下两种情况:
1.如果带BIND_AUTO_CREATE创建的服务会因为已重置,所以可以正常启动。如果remove后判断BIND_AUTO_CREATE这前出现异常。和2一样的。
2.如果不带,哪无论如何启动,都无法绑定服务
原因:
1 startServiceLocked-->unscheduleServiceRestartLocked(r,callingUid, false)
2 bindServiceLocked -->unscheduleServiceRestartLocked(s,callerApp.info.uid, false)
满足restartDelay重置要求uid不一样或在restartService队列里面,均不满足
接下来 ,他们分别都会调用到bringUpServiceLocked,且他们的启动传的wileRestarting都是false
1.startServiceInnerLocked -->bringUpServiceLocked(r,service.getFlags(), callerFg, false);
2.bindServiceLocked-->bringUpServiceLocked(s, service.getFlags(),callerFg, false)
然后restartDelay这个值是大于0.
这里直接进入return。无限死循环,永远都无法绑定。这就是问题产生的原因。
谷歌源代码中的解决已在6.0上更新:在这个方法中:bringUpServiceLocked中remove()成功后并进行重置可解决这个。
解决思路:当进程重启失败(removecontect时异常)时,因为上次成功时已remove且重置, delay值0.这时我们检测到没绑定,进行starft/bind就能绑定成功
也就是说:如果没有这个,哪start/bind永远也绑定不成功。
2.哪这样说:是不是进程只要这个地方出现异常。后面只要kill掉这个进程,后面永远也是无法绑定的。
哪为什么不执行解绑操作会加大出现的概率呢:我们看下解绑定的调用逻辑:
1.unbindServiceLocked(IServiceConnectionconnection)-->removeConnectionLocked(r, null, null)
-->bringDownServiceIfNeededLocked(s,true, hasAutoCreate)-->bringDownServiceLocked(r)
-->unscheduleServiceRestartLocked(r, 0,true)
2.stopServiceLockedà bringDownServiceIfNeededLocked()àbringDownServiceLockedà unscheduleServiceRestartLocked(r,0, true)
看到了吧,unbind会调用到这个,且传入的uid是为0,如果我们不调用,明显会出现restartDelay不会重置,出现这个情况的概率就会大大增加。而主动调用概率当然就会降低了。
但由于系统里面服务还是有很多状态的。比如说,如果出现服务hasAutoCreate创建返回为true,哪也是直接返回,就不会调用重置。其次,如果我调用stop的时候或Unbind的时候,出现此服务在mPendingServices队列里,这时unbind也是不会调用ret的,均直接return。
相比起来,serviceDoneExecutingLocked里多出了一个这个方法:
4.4以上的版本:见上面分析所用的serviceDoneExecutingLocked,参数上多了一个finishing,代码上多了一个r.app.remove,而这个正是罪魁祸首
4.4以下的版本:没有这个方法
其次还有一个问题区别:
如上面所说:如果要重置,会调用到这以下几个方法:
1 startServiceLocked-->unscheduleServiceRestartLocked(r,callingUid, false)
2 bindServiceLocked -->unscheduleServiceRestartLocked(s,callerApp.info.uid, false)
4.4以下:
每次启动必然会重置。所以4.4以后就算会失败,也不会出现这个问题
4.4以及以上:要满足以下条件,在restart列表里,但其实如果重启失败,会remove,但如果失败,刚上我上面所说,移除连续时异常,哪这个东西还没有加入到restart列表。
1.4.4到5.x的版本,其实只要重启过 ,delay都是不会重置的。除非你主动调用了stop或Unbind,如果是没有stop和unbind直接异常通出,也是会出现这种情况的
2.发生这个问题:必然是已经重启过一次,且重启成功了,后面再次被kill.重启失败才会产生