SalomonBrys / ANR-WatchDog

A simple watchdog that detects Android ANR (Application Not Responding) error and throws a meaningful exception
MIT License
2.87k stars 403 forks source link

Real ANR timeout #40

Open tprochazka opened 5 years ago

tprochazka commented 5 years ago

I tested your library and I found that reaction on ANR is much bigger than the value set in constructor by new ANRWatchDog(10000 /*timeout*/).start();

I found that it is caused by this code

        try {
            Thread.sleep(interval);
        } catch (InterruptedException e) {
            _interruptionListener.onInterrupted(e);
            return ;
        }

If there is no ANR, your thread basically sleeps for 10000 ms in this case. During this 10s can happen that thread is blocked, for example in the middle. So it is already 5s blocked at the end, but tick has arrived before it happens. So it will sleep again 10s, and then it detects ANR after 15s. So theoretically it can be almost double of configured time.

The only workaround is to use low timeout interval, for example, 500ms And then use ANRInterceptorand detect real 10s here.

But this behavior is not clear from the documentation.

tprochazka commented 5 years ago

Another weird thing is that timeout in the constructor is int, but time in ANRInterceptor is Long.

Vandalko commented 4 years ago

Oh, it helped me figure out why obvious Thread.sleep(7000) is not caught sometimes... Ended up with:

        new ANRWatchDog(500)
                .setANRInterceptor(new ANRWatchDog.ANRInterceptor() {

                    private long lastReportTs = 0L;

                    @Override
                    public long intercept(long duration) {
                        if (duration >= 2500L && System.currentTimeMillis() - lastReportTs > duration) {
                            final IllegalStateException exception = new IllegalStateException("ANR");
                            final Thread mainThread = Looper.getMainLooper().getThread();
                            exception.setStackTrace(mainThread.getStackTrace());
                            Log.e("ANRWatchDog",
                                    "ANR is detected",
                                    exception
                            );

                            lastReportTs = System.currentTimeMillis();
                        }

                        return Math.min(5000L - duration, 500L);
                    }
                })
                .setANRListener(error -> {
                    Log.e("ANRWatchDog",
                            "ANR is detected",
                            error
                    );
                })
                .start();