Created
September 12, 2014 18:46
-
-
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.
This file contains 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.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