Chromium Code Reviews| Index: base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
| diff --git a/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ad5cdd815bfcf81fe85952085f4f1656b04f185a |
| --- /dev/null |
| +++ b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
| @@ -0,0 +1,145 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.base; |
| + |
| +import android.animation.Animator; |
| +import android.animation.Animator.AnimatorListener; |
| +import android.animation.AnimatorListenerAdapter; |
| +import android.animation.TimeAnimator; |
| +import android.animation.TimeAnimator.TimeListener; |
| +import android.util.Log; |
| + |
| +/** |
| + * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring |
| + * any jankiness of short Chrome Android animations. It is limited to few seconds of recording. |
| + */ |
| +public class AnimationFrameTimeHistogram { |
| + private static final String TAG = "AnimationFrameTimeHistogram"; |
| + private static final int MAX_FRAME_TIME_NUM = 600; // 10 sec on 60 fps. |
| + |
| + private final Recorder mRecorder = new Recorder(); |
| + private final String mHistogramName; |
| + |
| + /** |
| + * @param histogramName The histogram name that the recorded frame times will be saved. |
| + * This must be also defined in histograms.xml |
| + * @return An AnimatorListener instance that records frame time histogram on start and end |
| + * automatically. |
| + */ |
| + public static AnimatorListener getAnimatorRecorder(final String histogramName) { |
| + return new AnimatorListenerAdapter() { |
| + private final AnimationFrameTimeHistogram mAnimationFrameTimeHistogram = |
| + new AnimationFrameTimeHistogram(histogramName); |
| + |
| + @Override |
| + public void onAnimationStart(Animator animation) { |
| + mAnimationFrameTimeHistogram.startRecording(); |
| + } |
| + |
| + @Override |
| + public void onAnimationEnd(Animator animation) { |
| + mAnimationFrameTimeHistogram.endRecording(); |
| + } |
| + |
| + @Override |
| + public void onAnimationCancel(Animator animation) { |
| + mAnimationFrameTimeHistogram.endRecording(); |
| + } |
| + }; |
| + } |
| + |
| + /** |
| + * @param histogramName The histogram name that the recorded frame times will be saved. |
| + * This must be also defined in histograms.xml |
| + */ |
| + public AnimationFrameTimeHistogram(String histogramName) { |
| + mHistogramName = histogramName; |
| + } |
| + |
| + /** |
| + * Start recording frame times. The recording can fail if it exceeds a few seconds. |
| + */ |
| + public void startRecording() { |
| + mRecorder.startRecording(); |
| + } |
| + |
| + /** |
| + * End recording and save it to histogram. It won't save histogram if the recording wasn't |
| + * successful. |
| + */ |
| + public void endRecording() { |
| + if (mRecorder.endRecording()) { |
| + nativeSaveHistogram(mHistogramName, |
| + mRecorder.getFrameTimesMs(), mRecorder.getFrameTimesCount()); |
| + } |
| + mRecorder.cleanUp(); |
| + } |
| + |
| + /** |
| + * Record Android animation frame rate and return the result. |
| + */ |
| + private static class Recorder implements TimeListener { |
| + // TODO(kkimlabs): If we can use in the future, migrate to Choreographer for minimal |
| + // workload. |
| + private final TimeAnimator mAnimator = new TimeAnimator(); |
| + private long[] mFrameTimesMs; |
| + private int mFrameTimesCount; |
| + |
| + private Recorder() { |
| + mAnimator.setTimeListener(this); |
| + } |
| + |
| + private void startRecording() { |
| + assert !mAnimator.isRunning(); |
| + mFrameTimesCount = 0; |
| + mFrameTimesMs = new long[MAX_FRAME_TIME_NUM]; |
| + mAnimator.start(); |
| + } |
| + |
| + /** |
| + * @return Whether the recording was successful. If successful, the result is available via |
| + * getFrameTimesNs and getFrameTimesCount. |
| + */ |
| + private boolean endRecording() { |
| + boolean succeeded = mAnimator.isStarted(); |
| + mAnimator.end(); |
| + return succeeded; |
| + } |
| + |
| + private long[] getFrameTimesMs() { |
| + return mFrameTimesMs; |
| + } |
| + |
| + private int getFrameTimesCount() { |
| + return mFrameTimesCount; |
| + } |
| + |
| + /** |
| + * Deallocates the temporary buffer to record frame times. Must be called after ending |
| + * the recording and getting the result. |
| + */ |
| + private void cleanUp() { |
| + mFrameTimesMs = null; |
| + } |
| + |
| + @Override |
| + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { |
| + if (mFrameTimesCount == mFrameTimesMs.length) { |
| + mAnimator.end(); |
| + cleanUp(); |
| + Log.w(TAG, "Animation frame time recording reached the maximum number. It's either" |
|
nyquist
2015/02/27 04:02:19
Is this intentionally a warning? I am just afraid
Kibeom Kim (inactive)
2015/02/27 19:56:40
I think spamming log will be unlikely to happen, s
|
| + + "the animation took too long or recording end is not called."); |
| + return; |
| + } |
| + |
| + // deltaTime is 0 for the first frame. |
| + if (deltaTime > 0) { |
| + mFrameTimesMs[mFrameTimesCount++] = deltaTime; |
| + } |
| + } |
| + } |
| + |
| + private native void nativeSaveHistogram(String histogramName, long[] frameTimesMs, int count); |
| +} |