<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android 流程度評(píng)測(cè)知多少

          共 10027字,需瀏覽 21分鐘

           ·

          2021-08-05 12:18

          和你一起終身學(xué)習(xí),這里是程序員Android

          經(jīng)典好文推薦,通過(guò)閱讀本文,您將收獲以下知識(shí)點(diǎn):

          一、FPS評(píng)測(cè)應(yīng)用流暢度不準(zhǔn)確
          二、Choreographer幀率檢測(cè)原理
          三、如何檢測(cè)

          一、FPS評(píng)測(cè)應(yīng)用流暢度不準(zhǔn)確

          說(shuō)到應(yīng)用的流暢度,都會(huì)想到FPS,系統(tǒng)獲取FPS的原理是:手機(jī)屏幕顯示的內(nèi)容是通過(guò)Android系統(tǒng)的SurfaceFLinger類(lèi),把當(dāng)前系統(tǒng)里所有進(jìn)程需要顯示的信息合成一幀,然后提交到屏幕上進(jìn)行顯示,F(xiàn)PS就是1秒內(nèi)SurfaceFLinger提交到屏幕的幀數(shù)。用FPS來(lái)評(píng)測(cè)一個(gè)應(yīng)用是否真的卡頓存在兩個(gè)問(wèn)題。

          • 有的時(shí)候FPS很低,APP看起來(lái)卻很流暢;

          • APP停止操作之后,F(xiàn)PS還是在一直變化,這種情況是否會(huì)影響到FPS的準(zhǔn)確度?
            有的時(shí)候FPS很低,APP看起來(lái)卻很流暢,是因?yàn)楫?dāng)前界面在1秒內(nèi)只需要10幀的顯示需求,當(dāng)然不會(huì)卡頓,此時(shí)FPS只要高于10就可以了,如果屏幕根本沒(méi)有繪制需求,那FPS的值就是0。

          Android性能優(yōu)化第(四)篇---Android渲染機(jī)制說(shuō)過(guò),Android系統(tǒng)每隔16ms發(fā)出VSYNC信號(hào),觸發(fā)對(duì)UI的渲染,16ms沒(méi)完成繪制就會(huì)卡頓。VSync機(jī)制就像是一臺(tái)轉(zhuǎn)速固定的發(fā)動(dòng)機(jī)(60轉(zhuǎn)/s)。每一轉(zhuǎn)會(huì)帶動(dòng)著去做一些UI相關(guān)的事情,但不是每一轉(zhuǎn)都會(huì)有工作去做(就像有時(shí)在空擋,有時(shí)在D檔)。有時(shí)候因?yàn)楦鞣N阻力某一圈工作量比較重超過(guò)了16.6ms,那么這臺(tái)發(fā)動(dòng)機(jī)這秒內(nèi)就不是60轉(zhuǎn)了,當(dāng)然也有可能被其他因素影響,比如給油不足(主線程里干的活太多)等等,就會(huì)出現(xiàn)轉(zhuǎn)速降低的狀況。我們把這個(gè)轉(zhuǎn)速叫做流暢度。當(dāng)流暢度越小的時(shí)候說(shuō)明當(dāng)前程序越卡頓。

          二、Choreographer幀率檢測(cè)原理

          我們有時(shí)候會(huì)看到這樣的log,系統(tǒng)幫助我們打印出了跳幀數(shù)。

          02-07 19:47:04.333 17601-17604/zhangwan.wj.com.choreographertest D/dalvikvm: GC_CONCURRENT freed 143K, 3% free 9105K/9384K, paused 2ms+0ms, total 6ms
          02-07 19:47:04.337 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 60 frames! The application may be doing too much work on its main thread.
          02-07 19:47:11.685 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 85 frames! The application may be doing too much work on its main thread.
          02-07 19:47:12.545 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 37 frames! The application may be doing too much work on its main thread.
          02-07 19:47:14.893 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 37 frames! The application may be doing too much work on its main thread.
          02-07 19:47:23.049 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 36 frames! The application may be doing too much work on its main thread.
          02-07 19:47:23.929 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 37 frames! The application may be doing too much work on its main thread.
          02-07 19:47:24.961 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 61 frames! The application may be doing too much work on its main thread.
          02-07 19:47:25.817 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 36 frames! The application may be doing too much work on its main thread.
          02-07 19:47:26.433 17601-17601/zhangwan.wj.com.choreographertest I/Choreographer: Skipped 36 frames! The application may be doing too much work on its main thread.

          這個(gè)log就出自于Choreographer中(英[?k?r?'?ɡr?f?(r)] 美[?k?r?'?ɡr?f?(r)])。

          void doFrame(long frameTimeNanos, int frame) {
          final long startNanos;
          synchronized (mLock) {
          if (!mFrameScheduled) {
          return; // no work to do
          }

          if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
          mDebugPrintNextFrameTimeDelta = false;
          Log.d(TAG, "Frame time delta: "
          + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
          }

          long intendedFrameTimeNanos = frameTimeNanos;
          startNanos = System.nanoTime();
          final long jitterNanos = startNanos - frameTimeNanos;
          if (jitterNanos >= mFrameIntervalNanos) {
          final long skippedFrames = jitterNanos / mFrameIntervalNanos;
          if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
          Log.i(TAG, "Skipped " + skippedFrames + " frames! "
          + "The application may be doing too much work on its main thread.");
          }
          final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
          if (DEBUG_JANK) {
          Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
          + "which is more than the frame interval of "
          + (mFrameIntervalNanos * 0.000001f) + " ms! "
          + "Skipping " + skippedFrames + " frames and setting frame "
          + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
          }
          frameTimeNanos = startNanos - lastFrameOffset;
          }
          }

          }

          其中SKIPPED_FRAME_WARNING_LIMIT是Choreographer的成員變量。

            // Set a limit to warn about skipped frames.
          // Skipped frames imply jank.
          private static final int SKIPPED_FRAME_WARNING_LIMIT =SystemProperties.getInt( "debug.choreographer.skipwarning", 30);

          也就是當(dāng)跳幀數(shù)大于設(shè)置的SKIPPED_FRAME_WARNING_LIMIT 值時(shí)會(huì)在當(dāng)前進(jìn)程輸出這個(gè)log。由于 SKIPPED_FRAME_WARNING_LIMIT 的值默認(rèn)為 30,所以上面的log并不是經(jīng)??吹剑绻覀冇梅瓷涞姆椒ò裇KIPPED_FRAME_WARNING_LIMIT的值設(shè)置成1,這樣可以保證只要有丟幀,就會(huì)有上面的log輸出來(lái)。

          static {
          try {
          Field field = Choreographer.class.getDeclaredField("SKIPPED_FRAME_WARNING_LIMIT");
          field.setAccessible(true);
          field.set(Choreographer.class,1);
          } catch (Throwable e) {
          e.printStackTrace();
          }
          }

          注意,這個(gè)方案是 API 16 以上才支持。Choreographer就是一個(gè)消息處理器,根據(jù)vsync 信號(hào) 來(lái)計(jì)算frame,而計(jì)算frame的方式就是處理三種回調(diào),包括事件回調(diào)、動(dòng)畫(huà)回調(diào)、繪制回調(diào)。這三種事件在消息輸入、加入動(dòng)畫(huà)、準(zhǔn)備繪圖layout 等動(dòng)作時(shí)均會(huì)發(fā)給Choreographer。一句話,我們只要捕獲這個(gè)log提取出skippedFrames 就可以知道界面是否卡頓。

          三、如何檢測(cè)

          采用上面的方式就可以在App內(nèi)部觀測(cè)當(dāng)前App的流暢度了。并且在丟幀的地方打印,就可以知道丟幀的大概原因,大概位置,定位代碼問(wèn)題。

          在Choreographer中有個(gè)回調(diào)接口,F(xiàn)rameCallback。

          public interface FrameCallback {  
          //當(dāng)新的一幀被繪制的時(shí)候被調(diào)用。
          public void doFrame(long frameTimeNanos);
          }

          根據(jù)上面的代碼,重寫(xiě)doFrame方法,所以照葫蘆畫(huà)瓢,自定義FrameCallback。我們可以在每一幀被渲染的時(shí)候記錄下它開(kāi)始渲染的時(shí)間,這樣在下一幀被處理時(shí),判斷上一幀在渲染過(guò)程中是否出現(xiàn)掉幀。

          public class SMFrameCallback implements Choreographer.FrameCallback {

          public static SMFrameCallback sInstance;

          private String TAG="SMFrameCallback";

          public static final float deviceRefreshRateMs=16.6f;

          public static long lastFrameTimeNanos=0;//納秒為單位

          public static long currentFrameTimeNanos=0;

          public void start() {
          Choreographer.getInstance().postFrameCallback(SMFrameCallback.getInstance());
          }

          public static SMFrameCallback getInstance() {
          if (sInstance == null) {
          sInstance = new SMFrameCallback();
          }
          return sInstance;
          }

          @Override
          public void doFrame(long frameTimeNanos) {
          if(lastFrameTimeNanos==0){
          lastFrameTimeNanos=frameTimeNanos;
          Choreographer.getInstance().postFrameCallback(this);
          return;
          }
          currentFrameTimeNanos=frameTimeNanos;
          float value=(currentFrameTimeNanos-lastFrameTimeNanos)/1000000.0f;

          final int skipFrameCount = skipFrameCount(lastFrameTimeNanos, currentFrameTimeNanos, deviceRefreshRateMs);
          Log.e(TAG,"兩次繪制時(shí)間間隔value="+value+" frameTimeNanos="+frameTimeNanos+" currentFrameTimeNanos="+currentFrameTimeNanos+" skipFrameCount="+skipFrameCount+"");
          lastFrameTimeNanos=currentFrameTimeNanos;
          Choreographer.getInstance().postFrameCallback(this);
          }

          /**
          *
          *計(jì)算跳過(guò)多少幀
          * @param start
          * @param end
          * @param devicefreshRate
          * @return
          */

          private int skipFrameCount(long start,long end,float devicefreshRate){
          int count =0;
          long diffNs=end-start;
          long diffMs = Math.round(diffNs / 1000000.0f);
          long dev=Math.round(devicefreshRate);
          if(diffMs>dev){
          long skipCount=diffMs/dev;
          count=(int)skipCount;
          }
          return count;
          }
          }

          在需要檢測(cè)的Activity中調(diào)用 SMFrameCallback.getInstance().start()即可。一般優(yōu)化一下,可以在BaseActivity去調(diào)用或者Activitylifecyclecallbacks中去調(diào)用.
          正常情況下輸出的日志是:

          02-07 20:18:52.605 6683-6683/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666  frameTimeNanos=6996166386820  currentFrameTimeNanos=6996166386820  skipFrameCount=0
          02-07 20:18:52.621 6683-6683/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=6996183053486 currentFrameTimeNanos=6996183053486 skipFrameCount=0
          02-07 20:18:52.637 6683-6683/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=6996199720152 currentFrameTimeNanos=6996199720152 skipFrameCount=0
          02-07 20:18:52.657 6683-6683/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=6996216386818 currentFrameTimeNanos=6996216386818 skipFrameCount=0
          02-07 20:18:52.673 6683-6683/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=6996233053484 currentFrameTimeNanos=6996233053484 skipFrameCount=0
          02-07 20:18:52.689 6683-6683/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=6996249720150 currentFrameTimeNanos=6996249720150 skipFrameCount=0

          有跳幀的時(shí)候輸出的日志是

          02-07 20:21:53.909 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666  frameTimeNanos=7177466379568  currentFrameTimeNanos=7177466379568  skipFrameCount=0
          02-07 20:21:53.925 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=7177483046234 currentFrameTimeNanos=7177483046234 skipFrameCount=0
          02-07 20:21:54.133 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=200.0 frameTimeNanos=7177683046226 currentFrameTimeNanos=7177683046226 skipFrameCount=11
          02-07 20:21:54.745 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=616.6666 frameTimeNanos=7178299712868 currentFrameTimeNanos=7178299712868 skipFrameCount=36
          02-07 20:21:54.757 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=7178316379534 currentFrameTimeNanos=7178316379534 skipFrameCount=0
          02-07 20:21:54.773 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=7178333046200 currentFrameTimeNanos=7178333046200 skipFrameCount=0
          02-07 20:21:54.789 9530-9530/zhangwan.wj.com.choreographertest E/SMFrameCallback: 兩次繪制時(shí)間間隔value=16.666666 frameTimeNanos=7178349712866 currentFrameTimeNanos=7178349712866 skipFrameCount=0

          看到兩次繪制的時(shí)間間隔相差616.6666毫秒,跳過(guò)了36幀,這個(gè)卡頓用戶是能夠明顯感知的

          參考鏈接:https://www.jianshu.com/p/d126640eccb1

          友情推薦:

          Android 開(kāi)發(fā)干貨集錦

          至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺(jué)得很優(yōu)秀,歡迎點(diǎn)擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請(qǐng)聯(lián)系小編刪除,歡迎您的建議與指正。同時(shí)期待您的關(guān)注,感謝您的閱讀,謝謝!

          點(diǎn)個(gè)在看,方便您使用時(shí)快速查找!

          瀏覽 86
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕在线观看第二页 | 欧美大码一区二区免费看 | 小早川怜子爆乿护士中文 | 国产午夜黄色视频 | 中文字幕乱 |