Home | History | Annotate | Download | only in base
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.base;
      6 
      7 import android.animation.Animator;
      8 import android.animation.Animator.AnimatorListener;
      9 import android.animation.AnimatorListenerAdapter;
     10 import android.animation.TimeAnimator;
     11 import android.animation.TimeAnimator.TimeListener;
     12 import android.util.Log;
     13 
     14 import org.chromium.base.annotations.MainDex;
     15 
     16 /**
     17  * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring
     18  * any jankiness of short Chrome Android animations. It is limited to few seconds of recording.
     19  */
     20 @MainDex
     21 public class AnimationFrameTimeHistogram {
     22     private static final String TAG = "AnimationFrameTimeHistogram";
     23     private static final int MAX_FRAME_TIME_NUM = 600; // 10 sec on 60 fps.
     24 
     25     private final Recorder mRecorder = new Recorder();
     26     private final String mHistogramName;
     27 
     28     /**
     29      * @param histogramName The histogram name that the recorded frame times will be saved.
     30      *                      This must be also defined in histograms.xml
     31      * @return An AnimatorListener instance that records frame time histogram on start and end
     32      *         automatically.
     33      */
     34     public static AnimatorListener getAnimatorRecorder(final String histogramName) {
     35         return new AnimatorListenerAdapter() {
     36             private final AnimationFrameTimeHistogram mAnimationFrameTimeHistogram =
     37                     new AnimationFrameTimeHistogram(histogramName);
     38 
     39             @Override
     40             public void onAnimationStart(Animator animation) {
     41                 mAnimationFrameTimeHistogram.startRecording();
     42             }
     43 
     44             @Override
     45             public void onAnimationEnd(Animator animation) {
     46                 mAnimationFrameTimeHistogram.endRecording();
     47             }
     48 
     49             @Override
     50             public void onAnimationCancel(Animator animation) {
     51                 mAnimationFrameTimeHistogram.endRecording();
     52             }
     53         };
     54     }
     55 
     56     /**
     57      * @param histogramName The histogram name that the recorded frame times will be saved.
     58      *                      This must be also defined in histograms.xml
     59      */
     60     public AnimationFrameTimeHistogram(String histogramName) {
     61         mHistogramName = histogramName;
     62     }
     63 
     64     /**
     65      * Start recording frame times. The recording can fail if it exceeds a few seconds.
     66      */
     67     public void startRecording() {
     68         mRecorder.startRecording();
     69     }
     70 
     71     /**
     72      * End recording and save it to histogram. It won't save histogram if the recording wasn't
     73      * successful.
     74      */
     75     public void endRecording() {
     76         if (mRecorder.endRecording()) {
     77             nativeSaveHistogram(mHistogramName,
     78                     mRecorder.getFrameTimesMs(), mRecorder.getFrameTimesCount());
     79         }
     80         mRecorder.cleanUp();
     81     }
     82 
     83     /**
     84      * Record Android animation frame rate and return the result.
     85      */
     86     private static class Recorder implements TimeListener {
     87         // TODO(kkimlabs): If we can use in the future, migrate to Choreographer for minimal
     88         //                 workload.
     89         private final TimeAnimator mAnimator = new TimeAnimator();
     90         private long[] mFrameTimesMs;
     91         private int mFrameTimesCount;
     92 
     93         private Recorder() {
     94             mAnimator.setTimeListener(this);
     95         }
     96 
     97         private void startRecording() {
     98             assert !mAnimator.isRunning();
     99             mFrameTimesCount = 0;
    100             mFrameTimesMs = new long[MAX_FRAME_TIME_NUM];
    101             mAnimator.start();
    102         }
    103 
    104         /**
    105          * @return Whether the recording was successful. If successful, the result is available via
    106          *         getFrameTimesNs and getFrameTimesCount.
    107          */
    108         private boolean endRecording() {
    109             boolean succeeded = mAnimator.isStarted();
    110             mAnimator.end();
    111             return succeeded;
    112         }
    113 
    114         private long[] getFrameTimesMs() {
    115             return mFrameTimesMs;
    116         }
    117 
    118         private int getFrameTimesCount() {
    119             return mFrameTimesCount;
    120         }
    121 
    122         /**
    123          * Deallocates the temporary buffer to record frame times. Must be called after ending
    124          * the recording and getting the result.
    125          */
    126         private void cleanUp() {
    127             mFrameTimesMs = null;
    128         }
    129 
    130         @Override
    131         public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
    132             if (mFrameTimesCount == mFrameTimesMs.length) {
    133                 mAnimator.end();
    134                 cleanUp();
    135                 Log.w(TAG, "Animation frame time recording reached the maximum number. It's either"
    136                         + "the animation took too long or recording end is not called.");
    137                 return;
    138             }
    139 
    140             // deltaTime is 0 for the first frame.
    141             if (deltaTime > 0) {
    142                 mFrameTimesMs[mFrameTimesCount++] = deltaTime;
    143             }
    144         }
    145     }
    146 
    147     private native void nativeSaveHistogram(String histogramName, long[] frameTimesMs, int count);
    148 }
    149