Skip to content

Instantly share code, notes, and snippets.

@ognian-
Created September 12, 2014 18:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ognian-/fe1743fe8b87fcb836f7 to your computer and use it in GitHub Desktop.
Save ognian-/fe1743fe8b87fcb836f7 to your computer and use it in GitHub Desktop.
Android: detect long blocks on the main thread, and log a stack trace. Construct the object on the main thread and check the log output. Pass the threshold to trigger a stack dump in milliseconds.
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
import android.util.Printer;
public class ANRDetector {
public static final String TAG = "ANRDetector";
private final Looper looper;
private final MessageQueue queue;
private final long threshold;
private volatile long lastIdle;
private volatile boolean isIdle;
private volatile boolean kill;
public ANRDetector() {
this(100);
}
public ANRDetector(long threshold) {
looper = Looper.myLooper();
queue = Looper.myQueue();
this.threshold = threshold;
lastIdle = System.currentTimeMillis();
queue.addIdleHandler(idleHandler);
looper.setMessageLogging(printer);
new Thread(watchdog, TAG).start();
}
public void kill() {
if (!kill) {
kill = true;
queue.removeIdleHandler(idleHandler);
looper.setMessageLogging(null);
synchronized (this) {
notify();
}
}
}
private final MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
isIdle = true;
synchronized (ANRDetector.this) {
ANRDetector.this.notify();
}
long delta = System.currentTimeMillis() - lastIdle;
if (delta >= threshold) Log.d(TAG, "Recoverd from block: " + delta + "ms");
return true;
}
};
private final Printer printer = new Printer() {
@Override
public void println(String x) {
if (isIdle) {
isIdle = false;
lastIdle = System.currentTimeMillis();
}
}
};
private final Runnable watchdog = new Runnable() {
@Override
public void run() {
final Thread targetThread = looper.getThread();
try {
while (!kill) {
synchronized (ANRDetector.this) {
ANRDetector.this.wait();
}
Thread.sleep(threshold);
if (isIdle) continue;
StringBuilder trace = new StringBuilder("Block threshold reached, stack trace:\n");
for (StackTraceElement ste : targetThread.getStackTrace()) {
trace.append(" ----> ");
trace.append(ste.toString());
trace.append('\n');
}
Log.d(TAG, trace.toString());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment