具体实施方式
下面结合附图和实施例,对本发明的具体实施方式作进一步详细描述。以下实施例用于说明本发明,但不用来限制本发明的范围。
如图1所示,一种获取应用卡顿信息的方法,包括:
S1,通过第一监听线程监听应用程序的特定方法;
S2,当所述第一监听线程检测到应用卡顿时,获取所述卡顿发生的第一开始时间和第一结束时间;
S3,基于所述第一开始时间和第一结束时间,在方法调用统计队列中获取所述特定方法的调用信息,以获取卡顿信息。
本实施例根据卡顿发生的开始时间和结束时间,从方法调用统计队列中找出这段卡顿时间应用程执行的方法,最终获得这段卡顿时间耗时的内特定方法的调用信息。本发明所述方法能够直接定位到引起问题发生的是哪个方法执行耗时导致的,只需要针对耗时方法进行修改和优化,省去大量定位问题和查找卡顿原因的时间和精力,大大提高了优化效率,进而提高了应用的流畅度,提升了应用的体验性。
本实施例所述特定方法,是应用程序中需要进行监听的任意一个方法,可以是应用程序主程序方法,也可以应用程序的一个用户界面的方法,需要对那个程序方法进行监听,就通过所述第一监听线程进行监听,本实施例对此不作限定。
作为一个可选的实施例,所述一种获取应用卡顿信息的方法还包括:
A1,通过第二监听线程监听开发包内所有方法的调用信息,并将所述调用信息存入所述方法调用统计队列中。
本实施例所述第二监听线程与第一监听线程各自独立执行,不具有同步关系,不存在交互,在上述S1、S2和S3执行的任意时间,本实施例第二监听线程都在执行,互相不影响。所述第一监听线程在监听到应用卡顿后,需要用到所述第二监听线程记录的方法调用统计队列的数据。
本实施例通过第二监听线程预先获取开发包内的所有方法的调用信息,并将所有方法的调用信息都记录下来,存入方法调用统计队列中,以便发生卡顿时可以通过预先记录的各方法的调用信息,来判断发生卡顿的程序方法。
所述开发包,是指实现应用程序的某个功能的开发代码。在一个应用程序中,根据不同的功能划分,可以有一个或多个开发包。本实施例中,所述开发包内的所有方法,可以是一个开发包的所有方法,也可以是多个开发包内的所有方法,也可以是全部开发包内的所有方法,所述S1中所述特定方法是所述开发包的所有方法中的一个方法。本实施例对此不作限定。当在多个开发包的所有方法进行监听时,比在一个开发包的所有方法进行监听,程序所消耗的资源会更多,具体选择哪一种方式可以根据具体需要而定。
作为一个可选的实施例,所述A1进一步包括:
A1.1,在所述应用程序执行时,获取所述开发包内所有方法的每一次执行的第二开始时间和第二结束时间;
A1.2,当所述第二结束时间和第二开始时间的差值大于预定统计时间值时,将所述方法的调用信息存入所述方法调用统计队列中。
具体的,所述调用信息包括:方法的类名、函数名和代码行。
具体的,所述方法调用统计队列的关键值为所述方法的耗时时间,所述耗时时间是指所述方法的第二结束时间与第二开始时间的差值。
本实施例第二监听线程伴随应用程序的执行过程而执行,持续记录应用程序执行时所有方法的每一次执行的开始时间和结束时间。由于应用程序中大部分方法都是正常的不耗时的,为了避免统计到的大量正常不耗时的方法影响问题的分析,本实施例将所述第二结束时间和第二开始时间的差值小于所述预定统计时间值的调用信息全部过滤,不保存,而只保存所述第二结束时间和第二开始时间的差值大于所述预定统计时间值,这样可以大大减少所述方法调用统计队列的数据量,减轻应用程序的统计压力,同时可以简化后面通过所述方法调用统计队列来查找卡顿信息,缩短分析时间。
本实施例中所述方法调用统计队列记录了应用执行时的每一个方法执行的顺序,时间点及耗时等信息。通过这样的记录,可以保证当卡顿发生时,根据卡顿发生的开始时间和结束时间从数据队列中获取到准确有效的程序方法,解决了卡顿发生时无法准确获取到方法的调用信息,无法准确定位到引起问题的方法。
本实施例中,具体的预定统计时间值,可以根据程序方法正常执行所需要消耗时间以及用户体验要求来定义,例如当一个特定方法正常执行需要16ms时,可以定义所述预定统计时间值为10ms。
那么,在应用程序执行过程中,所有执行耗时时间超过10ms的方法调用都将被记录到所述方法调用统计队列中,而所有执行耗时时间小于或等于10ms的方法调用都不会被记录到所述方法调用统计队列中。
所述方法调用统计队列中的一个记录值,包括关键值,方法的类名、函数名和代码行,其中所述关键值用于对所述方法调用统计队列进行检索,可以提高检索效率。
作为一个可选的实施例,所述S1进一步包括:
S1.1,在所述特定方法中切入第一监听执行点;
S1.2,利用所述第一监听执行点监听所述特定方法的执行过程。
本实施例中,在应用程序的需要监听的方法中,即一个具体的实现方法,也即所述特定方法,添加用于监听的代码,即第一监听执行点。则当所述特定方法执行时,所述第一监听执行点就会执行,从而进行监听。
作为一个可选的实施例,所述S2进一步包括:
S2.1,所述第一监听线程获取所述特定方法的执行持续时间;
S2.2,当所述执行持续时间大于预定卡顿时间值时,获取所述特定方法当前执行的开始时间为第一开始时间,获取当前时间为第一结束时间。
本实施例中,所述第一监听线程通过所述特定方法的执行持续时间来判断所述特定方法是否卡顿;当所述执行持续时间大于预定卡顿时间值时,即检测到应用卡顿,此时获取所述特定方法本次执行的开始时间为第一开始时间,获取当前时间为第一结束时间。
本实施例中,当检测到应用卡顿时,所述第一监听线程还可以获取所述特定方法的类名、函数名和当前执行的代码行等等,以便后面进行卡顿分析使用,具体根据后续的分析需要而定,本实施例对此不作限定。
本实施中,所述预定卡顿时间值可以根据所述特定方法正常执行所需要消耗时间以及用户体验要求来定义,例如当一个特定方法正常执行需要16ms时,所述预定卡顿时间值可以定为300ms。
那么,当所述第一监听线程监听到所述特定方法的持续执行时间超过300ms时,即检测到发生卡顿。
作为一个可选的实施例,所述S3进一步包括:
S3.1,在所述方法调用统计队列中,获取所述第一开始时间到第一结束时间的时间段内所述特定方法的全部调用信息;
S3.2,从所述全部调用信息中获取耗时时间大于所述预定卡顿时间值的调用信息。
本实施中,由于所述方法调用统计队列中包含了所有执行耗时时间大于预定统计时间的方法的调用信息,因此在第一步,通过卡顿发生的开始时间和接收时间,获取该时间段内的调用信息的集合,并且获取所述特定方法的调用信息的集合,然后从所述特定方法的调用信息的集合获取耗时时间大于所述预定卡顿时间值的调用信息,即具体定位到了是在所述特定方法的哪一次调用时发生了卡顿,实现了对应用卡顿问题的精确定位。
作为一个可选的实施例,所述A1.1之前还包括:
在所述开发包内所有方法的构造函数内切入监听执行点;
设置所述监听执行点在所述开发包内所有方法执行前和执行后都进行统计。
本实施例中,所述开发包是指需要进行卡顿问题定位的开发包,可以对应用程序中所有的开发包,也可以是针对一个具体应用的开发包,所述第二监听线程通过在应用程序的方法的构造函数中切入监听点,实现对方法的监听;当程序方法被调用执行,必然被监听到。
监听的方法是,在应用程序的方法执行前和执行后都进行统计,在执行前记录开始时间,及第二开始时间,在执行后记录结束时间,即第二结束时间。
下面通过一个具体实施例介绍本发明所述方法,该具体实施例的应用环境是安卓电视应用,安卓盒子应用或安卓手机应用,通过一个安卓应用的主界面来说明本发明所述方法;但本发明所述方法也可以应用于Windows操作***、Linux操作***或OS X操作***,本实施例对此不作具体限定。
本实施例在PC上开发,操作***为Android4.0,开发工具为Android Studio2.2,开发语言为java,所依赖的开发包为org.aspectj:aspectjtools:1.8.9,org.aspectj:aspectjweaver:1.8.9。
本实施例的具体实施包括:
1.通过AspectJ对应用代码的所有方法进行切入,监听所有方法的执行。
所述AspectJ是一种基于Java平台的面向切面的框架,可以对切面所连接的切入点织入代码,实现代码的注入。
通过定义Pointcut切入点切入代码,例如定义stackPointcut()方法连接点,注解@Pointcut("execution(*tv.douyu..*.*(..))"),其中@Pointcut表示切入点,execution表示方法和构造函数的执行点,*tv.douyu..*.*(..))表示包名是tv.douyu开头的所有方法,整个表示stackPointcut()方法切入包名是tv.douyu开头的所有方法的执行点。
通过切入应用的所有代码,可以实现对原应用代码的修改,便于监听原应用代码的执行。2.监听切入的应用代码执行点。
通过对切入点设置通知实现对方法的监听,例如定义stackMethod()方法,注解@Around("stackPointcut()"),其中@Around表示连接点执行前,执行完成后都通知,整个表示对步骤1中定义的stackPointcut()连接点执行前和执行后通知调用stackMethod()方法。
通过监听切入应用代码的执行点,可以实现对原应用代码执行时调用监听方法,从而可以实现对原应用方法执行统计信息。
3.统计原应用方法的执行信息。
实现步骤2中的stackMethod()方法,stackMethod()方法执行前获取方法开始时间startTime,startTime为此时代码执行时的当前时间System.currentTimeMillis(),再执行连接点的原方法,原应用方法执行完后,即stackMethod()执行结束前获取方法的结束时间endTime,endTime为此时代码执行时的当前时间System.currentTimeMillis()。
此时的开始时间startTime即为第二开始时间,结束时间endTime即为所述第二结束时间。
计算方法执行结束的时间和方法执行开始的时间的差值即为原应用方法的执行耗时时间elapsedTime,代码为elapsedTime=endTime-startTime;
同时获取原应用的方法类名className,方法函数名methodName,方法在代码中的行数methodLine,可通过切入点ProceedingJoinPoint获取,代码为方法类名className=proceedingJoinPoint.getSignature().getDeclaringType().getNa me(),方法函数名methodName=proceedingJoinPoint.getSignature().getName(),方法行数即对应的代码行methodLine=proceedingJoinPoint.getSignature.getSourceLocation().getLine()。
通过统计原应用方法的执行信息,当卡顿发生时就可以了解方法的详细信息,方法耗时了多久,是哪个类的那个方法导致的,在哪一行都有清晰的记录。
4.记录原应用方法的调用信息到数据队列中。
应用中大部分方法都是正常的不耗时的,为了避免统计到的大量正常不耗时的方法影响问题的分析,对调用信息进行过滤。
假设应用界面绘制的周期是16ms,则过滤掉方法耗时小于10ms(毫秒)的方法信息,即判断方法执行耗时elapsedTime是否小于10ms,若是则过滤掉。
过滤后,将步骤3中统计到的方法耗时elapsedTime,方法类名className,方法函数名methodName,方法行数methodLine信息保存到数据队列中,数据队列可采用***的LinkedHashMap链表哈希集合,其中key值为步骤3中的endTime方法执行完的时间,value内容为方法执行信息(elaspedTime,className,methodName,methosLine)。
本实施例中所述key值即所述关键值,所述数据队列即A1所述方法调用统计队列,所述10ms即A1.2中所述预定统计时间值。
通过记录原应用方法执行信息到数据队列中,可通过时间点获取到时间点对应的方法执行信息,当卡顿发生时就可以根据卡顿发生的开始时间和结束时间从数据队列中找出这段时间内的方法执行子队列,即卡顿时方法的调用执行情况。
通过步骤1,2,3,4对原应用所有方法切入代码,统计原应用运行时的方法耗时,方法类名,方法函数名,方法行数,并将统计到的原应用方法信息根据原方法执行结束的时间保存到数据队列中,记录了原应用执行时的每一个方法执行的顺序,时间点及耗时等信息。通过上述步骤可以保证当卡顿发生时,根据卡顿发生的开始时间和结束时间从数据队列中获取到准确有效的方法,解决了卡顿发生时无法准确获取到方法的调用信息,无法准确定位到引起问题的方法。
以上步骤1,2,3,4通过一个监听线程实现,即所述第二监听线程,以下步骤5、6通过另一个监听线程实现,即所述第一监听线程。
5.卡顿监听。
通过Choreographer.getInstance().postFrameCallback()的doFrame()回调方法,实时监听应用每一次的绘制情况,***的绘制周期为16ms,即每16ms时界面绘制一次,每隔16毫秒doFrame()回调执行一次。
本实施例中所述doFrame()回调方法即S1中所述特定方法。
通过计算应用实际的绘制周期,两次绘制轮询的间隔,即两次doFrame()执行的时间差就可判断应用是否有卡顿,一般认为绘制周期大于300ms时人眼能感受到明显的卡顿。
记录每次doFrame()的执行时间currentFrameTime和上一次doFrame()的执行时间lastFrameTime,当前绘制周期frameTime=currentFrameTime-lastFrameTime。
6.卡顿时抓取方法的调用信息。
若步骤5中frameTime大于300ms时,说明发生了卡顿,导致当前绘制距离上次绘制时间超过了300ms。根据步骤5中上次绘制时间lastFrameTime和当前绘制时间currentFrameTime,在步骤4中记录的原应用方法调用信息数据队列中查找出这段时间对应的方法的调用信息,即lastFrameTime到currentFrameTime这段超过300ms时间内的原应用方法调用信息。查找的方式通5过linkedHashMap.get(时间戳)的方式获取。
本实施例中,所述300ms即S2.2中所述预定卡顿时间值;所述上次绘制时间lastFrameTime即S2中所述第一开始时间,所述当前绘制时间currentFrameTime即S2中所述第一结束时间。
至此,一个应用卡顿问题被精确的定位了,开发和测试人员能够直接定位到引起问题发生的是哪个方法执行耗时导致的,只需要针对耗时方法进行修改和优化。
本发明还提供一种获取应用卡顿信息的装置,包括:
监听模块,用于通过第一监听线程监听应用程序的特定方法;
检测模块,用于当所述第一监听线程检测到应用卡顿时,获取所述卡顿发生的第一开始时间和第一结束时间;以及
获取模块,用于基于所述第一开始时间和第一结束时间,在方法调用统计队列中获取所述特定方法的调用信息,以获取卡顿信息。
本发明所述一种获取应用卡顿信息的装置还包括:
第二监听模块,用于通过第二监听线程监听开发包内所有方法的调用信息,并将所述调用信息存入所述方法调用统计队列中。
所述第二监听模块进一步包括:
获取时间单元,用于在所述应用程序执行时,获取所述开发包内所有方法的每一次执行的第二开始时间和第二结束时间;以及
统计单元,用于当所述第二结束时间和第二开始时间的差值大于预定统计时间值时,将所述方法的调用信息存入所述方法调用统计队列中。
所述调用信息包括:方法的类名、函数名和代码行。
所述方法调用统计队列的关键值为所述方法的耗时时间,所述耗时时间是指所述方法的第二结束时间与第二开始时间的差值。
所述检测模块进一步包括:
持续时间单元,用于所述第一监听线程获取所述特定方法的执行持续时间;以及
判断单元,用于当所述执行持续时间大于预定卡顿时间值时,获取所述特定方法当前执行的开始时间为第一开始时间,获取当前时间为第一结束时间。
所述获取模块进一步包括:
全部调用信息模块,用于在所述方法调用统计队列中,获取所述第一开始时间到第一结束时间的时间段内所述特定方法的全部调用信息;以及
定位模块,用于从所述全部调用信息中获取耗时时间大于所述预定卡顿时间值的调用信息。
本发明所述一种获取应用卡顿信息的方法及装置,能够直接定位到引起问题发生的是哪个方法执行耗时导致的,只需要针对耗时方法进行修改和优化,省去大量定位问题和查找卡顿原因的时间和精力,大大提高了优化效率,进而提高了应用的流畅度,提升了应用的体验性,具有良好的有益效果。
图2示出了本发明实施例获取应用卡顿信息的方法的测试设备的结构框图。
参照图2,所述获取应用卡顿信息的方法的测试设备,包括:处理器(processor)301、存储器(memory)302、通信接口(Communications Interface)303和总线304;
其中,
所述处理器301、存储器302、通信接口303通过所述总线304完成相互间的通信;
所述通信接口303用于该测试设备与获取应用卡顿信息的方法的通信设备之间的信息传输;
所述处理器301用于调用所述存储器302中的程序指令,以执行上述各方法实施例所提供的方法,例如包括:通过第一监听线程监听应用程序的特定方法;当所述第一监听线程检测到应用卡顿时,获取所述卡顿发生的第一开始时间和第一结束时间;基于所述第一开始时间和第一结束时间,在方法调用统计队列中获取所述特定方法的调用信息,以获取卡顿信息。
本发明另一实施例公开一种计算机程序产品,所述计算机程序产品包括存储在非暂态计算机可读存储介质上的计算机程序,所述计算机程序包括程序指令,当所述程序指令被计算机执行时,计算机能够执行上述各方法实施例所提供的方法,例如包括:通过第一监听线程监听应用程序的特定方法;当所述第一监听线程检测到应用卡顿时,获取所述卡顿发生的第一开始时间和第一结束时间;基于所述第一开始时间和第一结束时间,在方法调用统计队列中获取所述特定方法的调用信息,以获取卡顿信息。
本发明另一实施例提供一种非暂态计算机可读存储介质,所述非暂态计算机可读存储介质存储计算机指令,所述计算机指令使所述计算机执行上述各方法实施例所提供的方法,例如包括:通过第一监听线程监听应用程序的特定方法;当所述第一监听线程检测到应用卡顿时,获取所述卡顿发生的第一开始时间和第一结束时间;基于所述第一开始时间和第一结束时间,在方法调用统计队列中获取所述特定方法的调用信息,以获取卡顿信息。
本领域普通技术人员可以理解:实现上述方法实施例的全部或部分步骤可以通过程序指令相关的硬件来完成,前述的程序可以存储于一计算机可读取存储介质中,该程序在执行时,执行包括上述方法实施例的步骤;而前述的存储介质包括:ROM、RAM、磁碟或者光盘等各种可以存储程序代码的介质。
以上所描述的获取应用卡顿信息的方法的测试设备等实施例仅仅是示意性的,其中所述作为分离部件说明的单元可以是或者也可以不是物理上分开的,作为单元显示的部件可以是或者也可以不是物理单元,即可以位于一个地方,或者也可以分布到多个网络单元上。可以根据实际的需要选择其中的部分或者全部模块来实现本实施例方案的目的。本领域普通技术人员在不付出创造性的劳动的情况下,即可以理解并实施。
通过以上的实施方式的描述,本领域的技术人员可以清楚地了解到各实施方式可借助软件加必需的通用硬件平台的方式来实现,当然也可以通过硬件。基于这样的理解,上述技术方案本质上或者说对现有技术做出贡献的部分可以以软件产品的形式体现出来,该计算机软件产品可以存储在计算机可读存储介质中,如ROM/RAM、磁碟、光盘等,包括若干指令用以使得一台计算机设备(可以是个人计算机,服务器,或者网络设备等)执行各个实施例或者实施例的某些部分所述的方法。
最后,本申请的方法仅为较佳的实施方案,并非用于限定本发明的保护范围。凡在本发明的精神和原则之内,所作的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。