标准的FPS 是 1s 60 帧,也就是16.67ms一帧渲染完成,用户不会觉得卡顿。但是大部分应用是不需要1s内绘制60帧,我们需要根据丢帧率去计算fps,如果1s内应用只需要绘制30帧,但每帧的时间是不超过16.67ms,那么它的fps也是60。
当一帧渲染的时间大于16.67ms,按照垂直同步机制,该帧就已经渲染超时,那么,如果它正好是16.67的整数倍,比如66.68,则它花费了4个垂直同步脉冲,减去本身需要一个,则超时3个;如果它不是16.67的整数倍,比如67,那么它花费的垂直同步脉冲应向上取整,即5个,减去本身需要一个,即超时4个,可直接算向下取整
最后的计算方法思路:
执行一次命令,总共收集到了m帧(理想情况下m=128),但是这m帧里面有些帧渲染超过了16.67毫秒,算一次jank,一旦jank,
需要用掉额外的垂直同步脉冲。其他的就算没有超过16.67,也按一个脉冲时间来算(理想情况下,一个脉冲就可以渲染完一帧)
所以FPS的算法可以变为:
m / (m + 额外的垂直同步脉冲) * 60
Android性能测试之fps获取
安卓8.0之前使用dumpsys SurfaceFlinger 获取FPS信息详解(必看)
adb shell dumpsys SurfaceFlinger --latency
安卓9.0之后使用dumpsys gfxinfo framestats 获取FPS信息详解(必看)
adb shell dumpsys gfxinfo < PACKAGE_NAME > framestats
两种都是获取指定窗口的详细显示信息,需要了解每一列参数的详细信息
以9.0之后使用dumpsys gfxinfo 获取信息为例:
每一帧的时间就是 FRAME_COMPLETED - INTENDED_VSYNC ,时间是以ns为单位
由于是根据包名获取每一帧的详细信息,且最多只能获取128帧,所以最理想的状态就是2s内手机绘制了120帧,但大部分应用2s内是不会绘制120帧的,所以根据实际项目需要,我们需要设置不同的时间间隔去获取fps,此处我们使用2s间隔获取一次fps数据,相同时间内,绘制的帧越多,则fps越准确。
self.frequency = 2 取样频率 就是每间隔2秒获取一次fps信息
sn是设备id
_collector_thread 是用来获取fps信息的线程
_calculator_thread 是用来计算fps的线程
定义大于16.67ms为丢帧 jank
定义大于166.7ms为卡顿 caton
adb shell dumpsys activity | findstr mResume(适用于安卓9.0之后的,安卓8.0之前可使用adb shell dumpsys activity | findstr “mFocusedActivity”)
C:\Users\ysfa>adb shell dumpsys activity | findstr mResume
mResumedActivity: ActivityRecord{
db71ee8 u0 com.android.settings/.SubSettings t1572}
com.android.settings/.SubSettings 这个则是当前手机上活动的窗口名(包名省略为.)
注意! 部分三方应用的窗口会把包名全部显示,例如com.eg.android.AlipayGphone/com.alipay.mobile.security.login.ui.RecommandAlipayUserLoginActivity
C:\Users\ysfa>adb shell dumpsys activity | findstr mResume
mResumedActivity: ActivityRecord{
e84c958 u0 com.eg.android.AlipayGphone/com.alipay.mobile.security.login.ui.RecommandAlipayUserLoginActivity t1576}
拿到窗口名,我们可以提取出包名+窗口名的信息
因为使用shell dumpsys gfxinfo +包名+framestats 获取的这个进程的最近128帧的的详细信息,可能包含多个窗口(也可能包含重复帧),而信息中的窗口名是全名,没有.省略包名,所以我们需要自己拼出完整的窗口名
如将com.android.settings/.SubSettings 改成com.android.settings/com.android.settings.SubSettings
**
"adb shell dumpsys gfxinfo " + package_name + " reset"
例如下面则是有两个窗口
C:\Users\ysfa>adb shell dumpsys gfxinfo com.android.settings framestats
Applications Graphics Acceleration Info:
Uptime: 56776419 Realtime: 113853289
** Graphics info for pid 15490 [com.android.settings] **
Stats since: 43790991914179ns
Total frames rendered: 1021
Janky frames: 52 (5.09%)
50th percentile: 6ms
90th percentile: 11ms
95th percentile: 16ms
99th percentile: 77ms
Number Missed Vsync: 11
Number High input latency: 182
Number Slow UI thread: 30
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 1
Number Frame deadline missed: 31
HISTOGRAM: 5ms=486 6ms=157 7ms=112 8ms=65 9ms=49 10ms=27 11ms=33 12ms=18 13ms=13 14ms=2 15ms=6 16ms=2 17ms=2 18ms=0 19ms=4 20ms=1 21ms=1 22ms=0 23ms=4 24ms=1 25ms=1 26ms=2 27ms=1 28ms=3 29ms=0 30ms=0 31ms=0 32ms=3 34ms=0 36ms=1 38ms=1 40ms=4 42ms=2 44ms=3 46ms=0 48ms=0 53ms=2 57ms=2 61ms=0 65ms=0 69ms=1 73ms=1 77ms=3 81ms=2 85ms=0 89ms=1 93ms=0 97ms=0 101ms=0 105ms=1 109ms=1 113ms=0 117ms=0 121ms=0 125ms=1 129ms=0 133ms=0 150ms=2 200ms=0 250ms=0 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
Font Cache (CPU):
Size: 623.27 kB
Glyph Count: 41
CPU Caches:
GPU Caches:
Other:
Other: 0.00 bytes (1 entry)
Buffer Object: 2.05 KB (2 entries)
Image:
Texture: 151.12 KB (13 entries)
Scratch:
RenderTarget: 9.00 KB (1 entry)
Buffer Object: 48.00 KB (1 entry)
Texture: 4.00 MB (1 entry)
Other Caches:
Current / Maximum
VectorDrawableAtlas 0.00 kB / 0.00 KB (entries = 0)
Layers Total 0.00 KB (numLayers = 0)
Total GPU memory usage:
4409524 bytes, 4.21 MB (153.18 KB is purgeable)
Pipeline=Skia (OpenGL)
Layout Cache Info:
Usage: 513/5000 entries
Hit ratio: 15858/16371 (0.968664)
Profile data in ms:
com.android.settings/com.android.settings.Settings/android.view.ViewRootImpl@f131882 (visibility=8)
Window: com.android.settings/com.android.settings.Settings
Stats since: 52644835521687ns
Total frames rendered: 5
Janky frames: 2 (40.00%)
50th percentile: 5ms
90th percentile: 53ms
95th percentile: 53ms
99th percentile: 53ms
Number Missed Vsync: 0
Number High input latency: 2
Number Slow UI thread: 1
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 0
Number Frame deadline missed: 1
HISTOGRAM: 5ms=3 6ms=0 7ms=0 8ms=0 9ms=0 10ms=0 11ms=0 12ms=0 13ms=0 14ms=0 15ms=0 16ms=0 17ms=0 18ms=0 19ms=0 20ms=0 21ms=0 22ms=0 23ms=0 24ms=0 25ms=0 26ms=0 27ms=0 28ms=0 29ms=0 30ms=0 31ms=0 32ms=0 34ms=0 36ms=0 38ms=0 40ms=0 42ms=1 44ms=0 46ms=0 48ms=0 53ms=1 57ms=0 61ms=0 65ms=0 69ms=0 73ms=0 77ms=0 81ms=0 85ms=0 89ms=0 93ms=0 97ms=0 101ms=0 105ms=0 109ms=0 113ms=0 117ms=0 121ms=0 125ms=0 129ms=0 133ms=0 150ms=0 200ms=0 250ms=0 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
---PROFILEDATA---
Flags,IntendedVsync,Vsync,OldestInputEvent,NewestInputEvent,HandleInputStart,AnimationStart,PerformTraversalsStart,DrawStart,SyncQueued,SyncStart,IssueDrawCommandsStart,SwapBuffers,FrameCompleted,DequeueBufferDuration,QueueBufferDuration,
1,52644835725530,52644835725530,9223372036854775807,0,52644839507156,52644839521322,52644839522208,52644882804499,52644884679760,52644884854083,52644884964447,52644890317208,52644890604395,398000,115000,
0,52644852435274,52644885768606,9223372036854775807,0,52644891648406,52644891663354,52644892190333,52644892544864,52644894457364,52644894569864,52644894716947,52644896089499,52644896420489,323000,130000,
0,52644902567663,52644902567663,9223372036854775807,0,52644903024135,52644903036687,52644903037624,52644903244812,52644903515385,52644903659551,52644903796322,52644905614239,52644906009603,485000,155000,
0,52645654850958,52645654850958,9223372036854775807,0,52645655074707,52645655090697,52645655092832,52645655544968,52645655855541,52645656225228,52645656841530,52645659397416,52645659824343,353000,136000,
0,52645688284367,52645688284367,9223372036854775807,0,52645688486895,52645688497624,52645688498874,52645688666322,52645688838822,52645688971218,52645689081166,52645689787416,52645690071426,71000,102000,
---PROFILEDATA---
com.android.settings/com.android.settings.SubSettings/android.view.ViewRootImpl@3253b93 (visibility=8)
Window: com.android.settings/com.android.settings.SubSettings
Stats since: 52648079271686ns
Total frames rendered: 135
Janky frames: 9 (6.67%)
50th percentile: 7ms
90th percentile: 12ms
95th percentile: 23ms
99th percentile: 44ms
Number Missed Vsync: 1
Number High input latency: 39
Number Slow UI thread: 5
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 0
Number Frame deadline missed: 5
HISTOGRAM: 5ms=39 6ms=15 7ms=27 8ms=13 9ms=9 10ms=3 11ms=10 12ms=7 13ms=2 14ms=0 15ms=1 16ms=0 17ms=0 18ms=0 19ms=0 20ms=1 21ms=0 22ms=0 23ms=2 24ms=0 25ms=0 26ms=1 27ms=0 28ms=0 29ms=0 30ms=0 31ms=0 32ms=2 34ms=0 36ms=0 38ms=0 40ms=1 42ms=0 44ms=1 46ms=0 48ms=0 53ms=0 57ms=1 61ms=0 65ms=0 69ms=0 73ms=0 77ms=0 81ms=0 85ms=0 89ms=0 93ms=0 97ms=0 101ms=0 105ms=0 109ms=0 113ms=0 117ms=0 121ms=0 125ms=0 129ms=0 133ms=0 150ms=0 200ms=0 250ms=0 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0 650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0 1050ms=0 1100ms=0 1150ms=0 1200ms=0 1250ms=0 1300ms=0 1350ms=0 1400ms=0 1450ms=0 1500ms=0 1550ms=0 1600ms=0 1650ms=0 1700ms=0 1750ms=0 1800ms=0 1850ms=0 1900ms=0 1950ms=0 2000ms=0 2050ms=0 2100ms=0 2150ms=0 2200ms=0 2250ms=0 2300ms=0 2350ms=0 2400ms=0 2450ms=0 2500ms=0 2550ms=0 2600ms=0 2650ms=0 2700ms=0 2750ms=0 2800ms=0 2850ms=0 2900ms=0 2950ms=0 3000ms=0 3050ms=0 3100ms=0 3150ms=0 3200ms=0 3250ms=0 3300ms=0 3350ms=0 3400ms=0 3450ms=0 3500ms=0 3550ms=0 3600ms=0 3650ms=0 3700ms=0 3750ms=0 3800ms=0 3850ms=0 3900ms=0 3950ms=0 4000ms=0 4050ms=0 4100ms=0 4150ms=0 4200ms=0 4250ms=0 4300ms=0 4350ms=0 4400ms=0 4450ms=0 4500ms=0 4550ms=0 4600ms=0 4650ms=0 4700ms=0 4750ms=0 4800ms=0 4850ms=0 4900ms=0 4950ms=0
---PROFILEDATA---
Flags,IntendedVsync,Vsync,OldestInputEvent,NewestInputEvent,HandleInputStart,AnimationStart,PerformTraversalsStart,DrawStart,SyncQueued,SyncStart,IssueDrawCommandsStart,SwapBuffers,FrameCompleted,DequeueBufferDuration,QueueBufferDuration,
0,52649246791970,52649246791970,9223372036854775807,0,52649247008977,52649247016633,52649247083560,52649247252883,52649247318508,52649247419133,52649247507258,52649247726894,52649247943248,50000,68000,
0,52649263499755,52649263499755,9223372036854775807,0,52649263745904,52649263753456,52649263821425,52649263966112,52649264042935,52649264141008,52649264225852,52649264446789,52649264594394,52000,75000,
0,52649280172678,52649280172678,9223372036854775807,0,52649280390643,52649280398821,52649280492779,52649280826789,52649280901841,52649280991789,52649281092258,52649282058143,52649282244029,719000,84000,
0,52649296908974,52649296908974,9223372036854775807,0,52649297192571,52649297200956,52649297272987,52649297500748,52649297570018,52649297660071,52649297755123,52649298330800,52649298491789,388000,79000,
0,52649314226393,52649314226393,9223372036854775807,0,52649314576321,52649314590643,52649314682414,52649314878300,52649314976946,52649315080487,52649315199237,52649316256841,52649316463873,718000,87000,
0,52649330956704,52649330956704,9223372036854775807,0,52649331218456,52649331229706,52649331328925,52649331494654,52649331568821,52649331661529,52649331772883,52649332717258,52649332905748,693000,84000,
0,52649347049758,52649347049758,9223372036854775807,0,52649347281893,52649347293143,52649347379706,52649347608196,52649347693039,52649347778196,52649347890591,52649348834498,52649349042987,683000,102000,
0,52649697887620,52649697887620,9223372036854775807,0,52649702086373,52649702120643,52649702124862,52649703167310,52649708892727,52649709473560,52649710831060,52649717494654,52649718642466,499000,464000,
0,52649714600252,52649714600252,9223372036854775807,0,52649715011216,52649715042466,52649715046477,52649715644810,52649719492414,52649719910852,52649720709706,52649722900956,52649723371060,160000,184000,
0,52649731310975,52649731310975,9223372036854775807,0,52649731588612,52649731612362,52649731614758,52649732029341,52649734277570,52649734497466,52649735169029,52649737701372,52649738107883,160000,161000,
0,52649748021669,52649748021669,9223372036854775807,0,52649748340227,52649748360800,52649748363664,52649748845122,52649751609810,52649751923977,52649752743716,52649755433664,52649755953768,149000,243000,
0,52649764732139,52649764732139,9223372036854775807,0,52649765139289,52649765163195,52649765167258,52649765903091,52649769690539,52649770072206,52649770664966,52649774161997,52649774849550,193000,338000,
0,52649781442682,52649781442682,9223372036854775807,0,52649781792310,52649781814237,52649781818195,52649782385487,52649785698872,52649786096425,52649786659341,52649790374706,52649791046529,224000,310000,
0,52649798153253,52649798153253,9223372036854775807,0,52649798517831,52649798542050,52649798546060,52649799112883,52649802161268,52649802525435,52649803129602,52649806656581,52649807274602,240000,271000,
0,52649814863694,52649814863694,9223372036854775807,0,52649815799706,52649815828508,52649815832570,52649817335487,52649820929497,52649821328195,52649822128768,52649825968872,52649826741268,241000,395000,
0,52649831574226,52649831574226,9223372036854775807,0,52649831939654,52649831963300,52649831967414,52649832604602,52649835772466,52649836293404,52649836986112,52649840806581,52649841405018,223000,265000,
0,52649848284615,52649848284615,9223372036854775807,0,52649849011372,52649849041425,52649849045018,52649851157727,52649854439497,52649854777466,52649855739810,52649859079862,52649859603872,357000,228000,
0,52649864995380,52649864995380,9223372036854775807,0,52649865322362,52649865343664,52649865346425,52649865810487,52649868761529,52649869067883,52649869989289,52649873253768,52649873828560,151000,228000,
0,52649881706029,52649881706029,9223372036854775807,0,52649882549810,52649882577779,52649882580383,52649883775695,52649886574081,52649886904029,52649887672102,52649890712675,52649891259497,465000,218000,
0,52649898416683,52649898416683,9223372036854775807,0,52649898804237,52649898834810,52649898837675,52649899376841,52649902517570,52649902790695,52649903661320,52649906252206,52649906706841,149000,180000,
0,52649915127446,52649915127446,9223372036854775807,0,52649915427518,52649915452102,52649915454185,52649916730487,52649918999810,52649919207883,52649919950227,52649922103925,52649922502102,232000,163000,
0,52649931838312,52649931838312,9223372036854775807,0,52649932090852,52649932108508,52649932110279,52649932414654,52649934322987,52649934504445,52649935012831,52649936734237,52649937059185,111000,138000,
0,52649948548982,52649948548982,9223372036854775807,0,52649949961789,52649949987206,52649949990174,52649951342518,52649954262622,52649954549081,52649955380070,52649961340904,52649961895487,171000,228000,
0,52649965257509,52649965257509,9223372036854775807,0,52649966073612,52649966105956,52649966108820,52649966579862,52649968551477,52649968823560,52649969662414,52649973552102,52649974074654,958000,200000,
0,52649998612728,52649998612728,9223372036854775807,0,52649998977622,52649999557206,52649999559549,52650017059133,52650020417883,52650020646320,52650021568768,52650025146633,52650025758195,481000,200000,
0,52650015326027,52650015326027,9223372036854775807,0,52650021651945,52650021653299,52650022256320,52650022672935,52650023145487,52650025904237,52650026439497,52650028692154,52650029208664,104000,212000,
0,52650032044406,52650032044406,9223372036854775807,0,52650032325539,52650032351633,52650032628664,52650032995174,52650033231164,52650033404549,52650033809602,52650036246529,52650036571424,549000,144000,
0,52650048712678,52650048712678,9223372036854775807,0,52650049223247,52650049248352,52650049509706,52650050059966,52650050409289,52650050707310,52650051197727,52650052948872,52650053928299,303000,628000,
0,52650065451017,52650065451017,9223372036854775807,0,52650065783091,52650065811372,52650065992570,52650067972883,52650068491477,52650068892466,52650069592570,52650070378560,52650072193039,135000,1567000,
0,52650082148943,52650082148943,9223372036854775807,0,52650082448872,52650082474810,52650082711737,52650084045956,52650084379341,52650084573508,52650085195070,52650085874081,52650086263404,119000,172000,
0,52650098856086,52650098856086,9223372036854775807,0,52650099223768,52650099243977,52650099439602,52650099924549,52650101026008,52650101260956,52650101923872,52650102694966,52650103103768,157000,166000,
0,52650115562739,52650115562739,9223372036854775807,0,52650115944706,52650115969341,52650116332310,52650116949081,52650117979133,52650118371424,52650118993977,52650120497466,52650121246477,340000,302000,
0,52650132269055,52650132269055,9223372036854775807,0,52650132743247,52650132778091,52650133080070,52650133797727,52650135069497,52650135579133,52650136537727,52650138084862,52650138892154,492000,336000,
0,52650148975500,52650148975500,9223372036854775807,0,52650149621216,52650149650226,52650149885174,52650150524654,52650151176372,52650151718091,52650152564914,52650153554601,52650154188664,193000,308000,
0,52650165681977,52650165681977,9223372036854775807,0,52650166140122,52650166172154,52650166478143,52650167273612,52650168604758,52650169180226,52650169986893,52650171338299,52650172238247,370000,447000,
0,52650182388296,52650182388296,9223372036854775807,0,52650182942779,52650182972362,52650183367101,52650183978456,52650185713039,52650186415591,52650187213195,52650189186529,52650189928716,924000,340000,
0,52650199094781,52650199094781,9223372036854775807,0,52650199689549,52650199830956,52650200480226,52650200939445,52650202084914,52650202350226,52650203078404,52650203995383,52650204613351,215000,218000,
0,52650215800540,52650215800540,9223372036854775807,0,52650216403976,52650216429393,52650217027206,52650217563716,52650218779810,52650219244497,52650219859706,52650221868820,52650223560747,676000,1300000,
0,52650232507063,52650232507063,9223372036854775807,0,52650232925226,52650232957935,52650233205226,52650233922570,52650234452726,52650234798508,52650235375174,52650236484289,52650237067518,223000,261000,
0,52650249213762,52650249213762,9223372036854775807,0,52650249567049,52650249631841,52650249828247,52650250196841,52650250466216,52650252292518,52650253297466,52650254284654,52650254833195,208000,256000,
---PROFILEDATA---
View hierarchy:
com.android.settings/com.android.settings.Settings/android.view.ViewRootImpl@f131882
255 views, 270.94 kB of display lists
com.android.settings/com.android.settings.SubSettings/android.view.ViewRootImpl@3253b93
274 views, 291.13 kB of display lists
Total ViewRootImpl: 2
Total Views: 529
Total DisplayList: 562.06 kB
例如下面则是有两个窗口
Window: com.android.settings/com.android.settings.SubSettings(窗口一)
Window: com.android.settings/com.android.settings.Settings(窗口二)
我们需要根据上面获取的当前活动窗口(包名不能省略,如果包名省略了,需要转换为全名),去筛选出当前正在活动窗口的fps信息,也就是PROFILEDATA的中间数据
原理:根据返回的信息中含有window去定位到当前窗口,第一个—PROFILEDATA— 中的数据就是当前窗口的详细信息
详细方法看_get_fps_data
---PROFILEDATA---
Flags,IntendedVsync,Vsync,OldestInputEvent,NewestInputEvent,HandleInputStart,AnimationStart,PerformTraversalsStart,DrawStart,SyncQueued,SyncStart,IssueDrawCommandsStart,SwapBuffers,FrameCompleted,DequeueBufferDuration,QueueBufferDuration,
1,52644835725530,52644835725530,9223372036854775807,0,52644839507156,52644839521322,52644839522208,52644882804499,52644884679760,52644884854083,52644884964447,52644890317208,52644890604395,398000,115000,
0,52644852435274,52644885768606,9223372036854775807,0,52644891648406,52644891663354,52644892190333,52644892544864,52644894457364,52644894569864,52644894716947,52644896089499,52644896420489,323000,130000,
0,52644902567663,52644902567663,9223372036854775807,0,52644903024135,52644903036687,52644903037624,52644903244812,52644903515385,52644903659551,52644903796322,52644905614239,52644906009603,485000,155000,
0,52645654850958,52645654850958,9223372036854775807,0,52645655074707,52645655090697,52645655092832,52645655544968,52645655855541,52645656225228,52645656841530,52645659397416,52645659824343,353000,136000,
0,52645688284367,52645688284367,9223372036854775807,0,52645688486895,52645688497624,52645688498874,52645688666322,52645688838822,52645688971218,52645689081166,52645689787416,52645690071426,71000,102000,
---PROFILEDATA---
记录当前窗口的最后一次时间(第一次取0,可能会存在一些误差,所以测试之前我们需要清除数据),遍历PROFILEDATA中IntendedVsync(每帧的起始时间)的时间大于最后一次记录的时间,这时候就不是重复帧,才进行采集数据
# 我们需要在次数去除重复帧,通过每帧的起始时间去判断是否是重复的
for timestamp in each_frame_timestamps:
if timestamp[0] > self.last_timestamp:
timestamps.append((timestamp[1] - timestamp[0]) / 1000000)
self.last_timestamp = timestamp[0]
return timestamps, int(phone_time)
每帧耗时时间 time = (FRAME_COMPLETED - INTENDED_VSYNC)/1000000 (单位ms)
最终去除重复帧后,集合中有多少行就是有多少帧绘制,大于16.67ms的则是jank ,大于166.7ms则是卡顿
总共收集到了m帧(理想情况下m=128),但是这m帧里面有些帧渲染超过了16.67毫秒,算一次jank,一旦jank,
需要用掉额外的垂直同步脉冲。其他的就算没有超过16.67,也按一个脉冲时间来算(理想情况下,一个脉冲就可以渲染完一帧)
所以FPS的算法可以变为:
m / (m + 额外的垂直同步脉冲) * 60
FpsInfo 是结构体
FpsListenserImpl 是定义的接口实现,用来调用显示和打印的,调试时可删除
功能就是每隔2s获取当前活动窗口的fps,并统计丢帧数和丢帧的耗时时间
执行结果如下
C:\Users\ysfa\AppData\Local\Programs\Python\Python37\python.exe D:/PycharmProjects/untitled/fps.py
FPS monitor has start!
已经清除FPS数据,请3秒后开始滑动界面
当前进程是:com.android.settings
当前窗口是:com.android.settings/com.android.settings.SubSettings
当前手机窗口刷新时间:2020-07-30 19:59:57
当前窗口fps是:59.0625
当前2s获取总帧数:63
当前窗口丢帧数>16.67ms)是:1
[24.982251]
当前窗口卡顿数(>166.7ms)是:0
当前进程是:com.android.settings
当前窗口是:com.android.settings/com.android.settings.SubSettings
当前手机窗口刷新时间:2020-07-30 19:59:59
当前窗口fps是:60.0
当前2s获取总帧数:41
当前窗口丢帧数>16.67ms)是:0
[]
当前窗口卡顿数(>166.7ms)是:0
import os
import queue
import threading
import time
from math import floor
from FpsInfo import FpsInfo
from FpsListenserImpl import FpsListenserImpl
class FPSMonitor(object):
def __init__(self, sn):
self.frequency = 2 # 取样频率
self.device = sn
self.fpscollector = FpsCollector(self.device, self.frequency)
def set_listener(self, listener):
self.fpscollector.set_listener(listener)
def start(self, start_time):
self.start_time = start_time
if self.fpscollector.package_name is None:
print("手机没亮屏,或者usb未连接")
return
print('FPS monitor has start!')
self.fpscollector.start(start_time)
def stop(self):
'''结束FPSMonitor日志监控器
'''
if self.fpscollector.package_name is None:
print("手机没亮屏,或者usb未连接")
return
self.fpscollector.stop()
print('FPS monitor has stop!')
def save(self):
pass
def parse(self, file_path):
'''解析
:param str file_path: 要解析数据文件的路径
'''
pass
def get_fps_collector(self):
'''获得fps收集器,收集器里保存着time fps jank的列表
:return: fps收集器
:rtype: SurfaceStatsCollector
'''
return self.fpscollector
class FpsCollector(object):
'''Collects surface stats for a SurfaceView from the output of SurfaceFlinger
'''
def __init__(self, device, frequency):
self.device = device
self.frequency = frequency
self.jank_threshold = 16.7 # 内部的时间戳是毫秒秒为单位
self.last_timestamp = 0 # 上次最后最早一帧的时间
self.data_queue = queue.Queue()
self.stop_event = threading.Event()
self.get_focus_window()
self.listener = None
# queue 上报线程用
def start(self, start_time):
'''打开SurfaceStatsCollector
'''
self._clear_fps_data()
self.collector_thread = threading.Thread(target=self._collector_thread)
self.collector_thread.start()
self.calculator_thread = threading.Thread(target=self._calculator_thread, args=(start_time,))
self.calculator_thread.start()
def stop(self):
'''结束SurfaceStatsCollector
'''
if self.collector_thread:
self.stop_event.set()
self.collector_thread.join()
self.collector_thread = None
def set_listener(self, listener):
self.listener = listener
def get_focus_window(self):
'''通过adb shell dumpsys activity | findstr "mResume"
'''
cmd = "adb -s " + self.device + " shell dumpsys activity | findstr mResume"
# print(cmd)
windowInfo = os.popen(cmd)
windowInfo = str(windowInfo.readline())
# print(windowInfo)
if windowInfo is "":
self.package_name = None
self.focus_window = None
return
packageNameinfo = windowInfo.split(" ")[7]
packageName = packageNameinfo.split("/")[0]
if "/." in packageNameinfo:
windowName = packageName + "/" + packageName + "." + packageNameinfo.split("/.")[1]
else:
windowName = packageNameinfo
self.package_name = packageName
self.focus_window = windowName
def _calculate_results(self, timestamps):
"""Returns a list of SurfaceStatsCollector.Result.
FPS 丢帧率 卡顿次数 总帧数
"""
frame_count = len(timestamps)
jank_list, caton, vsyncOverTimes = self._calculate_janky(timestamps)
fps = frame_count / (frame_count + vsyncOverTimes) * 60
return fps, jank_list, caton
def _calculate_janky(self, timestamps):
# 统计丢帧卡顿 ,和需要垂直同步次数
jank_list = []
caton = 0
vsyncOverTimes = 0
for timestamp in timestamps:
if timestamp > self.jank_threshold:
# 超过16.67ms
jank_list.append(timestamp)
if timestamp % self.jank_threshold == 0:
vsyncOverTimes += ((timestamp / self.jank_threshold) - 1)
else:
vsyncOverTimes += floor(timestamp / self.jank_threshold)
if timestamp > self.jank_threshold * 10:
# 超过166.7ms 明显卡的帧,用户会觉得卡顿
caton += 1
return jank_list, caton, vsyncOverTimes
def _calculator_thread(self, start_time):
'''处理surfaceflinger数据
'''
while True:
try:
data = self.data_queue.get()
# print(data)
if isinstance(data, str) and data == 'Stop':
break
# before = time.time()
refresh_time = int(data[0])
# print(refresh_time)
timestamps = data[1]
fps, jank_list, caton = self._calculate_results(timestamps)
fps_info = FpsInfo(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(refresh_time)), len(timestamps),
fps,
self.package_name, self.focus_window, jank_list, len(jank_list), caton)
self.listener.report_fps_info(fps_info)
# print('\n')
# print("当前设备是:" + self.device)
# print("当前进程是:" + self.package_name)
# print("当前窗口是:" + self.focus_window)
# print("当前手机窗口刷新时间:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(refresh_time)))
# print("当前窗口fps是:" + str(fps))
# print("当前2s获取总帧数:" + str(len(timestamps)))
# print("当前窗口丢帧数>16.67ms)是:" + str(jank_list))
# print("当前窗口卡顿数(>166.7ms)是:" + str(caton))
# print('\n')
except Exception as e:
print(e)
print("计算fps数据异常")
self.data_queue.put('Stop')
if self.calculator_thread:
self.stop_event.set()
self.calculator_thread = None
return
def _collector_thread(self):
'''收集FPS数据
shell dumpsys gfxinfo 《 window》 framestats
3
'''
last_refresh_time = 0
while not self.stop_event.is_set():
# 此处进行获取,并将数据存放在data_quue里
try:
before = time.time()
# print("开始获取fps信息:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(before)))
self.get_focus_window()
new_timestamps, now_refresh_time = self._get_fps_data()
# 此处需要检查是否获取数据成功
if now_refresh_time is None or new_timestamps is None:
# 这里可能就是清楚数据后,没有做界面操作,所以会拿不到数据,跳过,我们获取下一次的
continue
# print(new_timestamps)
# 大于则证明有帧信息刷新,无则不需要更新信息
if self.last_timestamp > last_refresh_time:
last_refresh_time = self.last_timestamp
# print(last_refresh_time)
self.data_queue.put((now_refresh_time, new_timestamps))
time_consume = time.time() - before
delta_inter = self.frequency - time_consume
if delta_inter > 0:
time.sleep(delta_inter)
# print('\n')
# print("结束获取fps信息:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())))
except Exception as e:
print("获取fps数据异常")
print(e)
self.data_queue.put('Stop')
if self.collector_thread:
self.stop_event.set()
self.collector_thread = None
return
if self.stop_event.is_set():
self.data_queue.put('Stop')
def _clear_fps_data(self):
os.popen("adb -s " + self.device + " shell dumpsys gfxinfo " + self.package_name + " reset")
# 清除数据后,直接获取fps会有异常,我们最好等待一段时间
print("已经清除FPS数据,请3秒后开始滑动界面")
time.sleep(5)
def _get_fps_data(self):
"""
isHaveFoundWindow 是否是当前活动窗口
total_frames 总帧数
timestamps 每帧耗时信息
:rtype:
:return:
"""
results = os.popen("adb -s " + self.device + " shell dumpsys gfxinfo " + self.package_name + " framestats")
phone_time = os.popen("adb -s " + self.device + " shell date +%s")
phone_time = phone_time.readlines()[0]
# print(phone_time)
timestamps = []
each_frame_timestamps = []
isHaveFoundWindow = False
PROFILEDATA_line = 0
# 行数代表当前窗口总帧数,列数是每帧耗时详细信息
# !!!注意一个进程可能存在多个窗口,所以我们只获取当前显示窗口的信息
for line in results.readlines():
# print("test" + line)
if "Window" in line and self.focus_window in line:
isHaveFoundWindow = True
# print("focus Window is :" + line)
continue
if isHaveFoundWindow and "---PROFILEDATA---" in line:
PROFILEDATA_line += 1
# print(PROFILEDATA_line)
continue
if isHaveFoundWindow and "Flags,IntendedVsync," in line:
continue
if isHaveFoundWindow and PROFILEDATA_line is 1:
# 此处代表的是当前活动窗口
# 我们取PROFILEDATA中间的数据 最多128帧,还可能包含之前重复的帧,所以我们间隔1.5s就取一次数据
fields = []
fields = line.split(",")
each_frame_timestamp = [float(fields[1]), float(fields[13])]
each_frame_timestamps.append(each_frame_timestamp)
continue
if PROFILEDATA_line >= 2:
break
# 我们需要在次数去除重复帧,通过每帧的起始时间去判断是否是重复的
for timestamp in each_frame_timestamps:
if timestamp[0] > self.last_timestamp:
timestamps.append((timestamp[1] - timestamp[0]) / 1000000)
self.last_timestamp = timestamp[0]
return timestamps, int(phone_time)
def run_adb(cmd):
return os.popen(cmd)
if __name__ == '__main__':
sn = "9365fc0e"
monitor = FPSMonitor(sn)
lisenter = FpsListenserImpl()
monitor.set_listener(lisenter)
monitor.start(time.time())
time.sleep(60)
monitor.stop()
#!/usr/bin/env python
# coding:utf-8
"""
Name : FpsInfo.py
Author :
Contect :
Time : 2020/7/21 14:26
Desc:
"""
class FpsInfo(object):
def __init__(self, time, total_frames, fps, pkg_name, window_name, jankys_ary, jankys_more_than_16,
jankys_more_than_166):
# 采样数据时的时间戳
self.time = time
# 2s内取到总帧数
self.total_frames = total_frames
# fps
self.fps = fps
# 测试应用包名
self.pkg_name = pkg_name
# 窗口名
self.window_name = window_name
# 掉帧具体时间集合
self.jankys_ary = jankys_ary
# 掉帧数目,大于16.67ms
self.jankys_more_than_16 = jankys_more_than_16
# 卡顿数目,大于166.7ms
self.jankys_more_than_166 = jankys_more_than_166
#!/usr/bin/env python
# coding:utf-8
"""
Name : FpsListener.py
Author :
Contect :
Time : 2020/7/23 16:07
Desc:
"""
from abc import ABCMeta, abstractmethod
class IFpsListener(object):
@abstractmethod
def report_fps_info(self, fps_info):
pass
from FpsListener import IFpsListener
class FpsListenserImpl(IFpsListener):
def __init__(self):
pass
def report_fps_info(self, fps_info):
print('\n')
# print("当前设备是:" + fps_info.)
print("当前进程是:" + str(fps_info.pkg_name))
print("当前窗口是:" + str(fps_info.window_name))
print("当前手机窗口刷新时间:" + str(fps_info.time))
print("当前窗口fps是:" + str(fps_info.fps))
print("当前2s获取总帧数:" + str(fps_info.total_frames))
print("当前窗口丢帧数>16.67ms)是:" + str(fps_info.jankys_more_than_16))
print(fps_info.jankys_ary)
print("当前窗口卡顿数(>166.7ms)是:" + str(fps_info.jankys_more_than_166))
print('\n')