Skip to content

Instantly share code, notes, and snippets.

@runningcode
Last active March 18, 2016 17:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save runningcode/fc489fb09fca0dcd8497 to your computer and use it in GitHub Desktop.
Save runningcode/fc489fb09fca0dcd8497 to your computer and use it in GitHub Desktop.
Skipped Frames Counter
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