Last active
March 18, 2016 17:08
-
-
Save runningcode/fc489fb09fca0dcd8497 to your computer and use it in GitHub Desktop.
Skipped Frames Counter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.view.Choreographer; | |
import timber.log.Timber; | |
import java.lang.ref.WeakReference; | |
import java.lang.reflect.Field; | |
/** | |
* This class is based on a talk by Jason Sendros (Facebook) at Droidcon NYC 2015. | |
* This class keeps track of the number of skipped frames and the number of skipped frames | |
* caused by GC events. | |
*/ | |
public class SkippedFramesCounter { | |
private static final long NANOS_PER_MILLI = 1000000000; | |
private static final int DEFAULT_FRAME_INTERVAL = 16666667; | |
private static final int FIRST_FRAME = -1; | |
private WeakReference<Object> gcReference; | |
private long previousFrame = FIRST_FRAME; | |
private long frameInterval; | |
// total counts | |
private long totalGCSkippedFrames; | |
private long totalSkippedFrames; | |
private long totalFrames; | |
private static final SkippedFramesCounter INSTANCE = new SkippedFramesCounter(); | |
public static SkippedFramesCounter getInstance() { | |
return INSTANCE; | |
} | |
private SkippedFramesCounter() { | |
try { | |
Field f = Choreographer.getInstance().getClass().getDeclaredField("mFrameIntervalNanos"); | |
f.setAccessible(true); | |
frameInterval = (Long) f.get(Choreographer.getInstance()); | |
Timber.i("Frame interval is %d", frameInterval); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
throw new IllegalStateException(e); | |
} | |
setRef(); | |
} | |
public void start() { | |
previousFrame = FIRST_FRAME; | |
Choreographer.getInstance().postFrameCallback(callback); | |
} | |
public void stop() { | |
Choreographer.getInstance().removeFrameCallback(callback); | |
} | |
private final Choreographer.FrameCallback callback = new Choreographer.FrameCallback() { | |
@Override | |
public void doFrame(long frameTimeNanos) { | |
boolean gc = hasGCOccurred(); | |
if (previousFrame == FIRST_FRAME) { | |
Choreographer.getInstance().postFrameCallback(callback); | |
previousFrame = frameTimeNanos; | |
return; | |
} | |
long frameTime = frameTimeNanos - previousFrame; | |
long skippedFrames = (frameTime / frameInterval) - 1; | |
if (skippedFrames > 0) { | |
if (gc) { | |
totalGCSkippedFrames += skippedFrames; | |
} | |
totalSkippedFrames += skippedFrames; | |
totalFrames += skippedFrames; | |
} | |
previousFrame = frameTimeNanos; | |
Choreographer.getInstance().postFrameCallback(callback); | |
totalFrames ++; | |
} | |
}; | |
public long getTotalGCSkippedFrames() { | |
return totalGCSkippedFrames; | |
} | |
public long getTotalFrames() { | |
return totalFrames; | |
} | |
public long getTotalSkippedFrames() { | |
return totalSkippedFrames; | |
} | |
public float getPercentSkippedFrames() { | |
if (totalFrames == 0) { | |
return 0; | |
} | |
return 1.0f * totalSkippedFrames / totalFrames; | |
} | |
private boolean hasGCOccurred() { | |
if (gcReference.get() == null) { | |
setRef(); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
private void setRef() { | |
gcReference = new WeakReference<>(new Object()); | |
} | |
public void resetCounts() { | |
totalFrames = 0; | |
totalGCSkippedFrames = 0; | |
totalSkippedFrames = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment