Giter Site home page Giter Site logo

xamarin.anrwatchdog's Introduction

CodeFactor Build status NuGet version

Xamarin.ANRWatchDog

Xamarin port of https://github.com/SalomonBrys/ANR-WatchDog

A simple watchdog that detects Android ANRs (Application Not Responding).

Table of contents

Why it exists

There is currently no way for an android application to catch and report ANR errors.
If your application is not in the play store (either because you are still developing it or because you are distributing it differently), the only way to investigate an ANR is to pull the file /data/anr/traces.txt.
Additionally, we found that using the Play Store was not as effective as being able to choose our own bug tracking service.

There is an issue entry in the android bug tracker describing this lack, feel free to star it ;)

What it does

It sets up a "watchdog" timer that will detect when the UI thread stops responding. When it does, it raises an error with all threads stack traces (main first).

Can it work with crash reporters?

Yes! I'm glad you asked: That's the reason why it was developed in the first place!
As this throws an error, a crash handler can intercept it and handle it the way it needs.

Known working crash reporters include:

And there is no reason why it should not work with [insert your favourite crash reporting system here].

How it works

The watchdog is a simple thread that does the following in a loop:

  1. Schedules a runnable to be run on the UI thread as soon as possible.
  2. Wait for 5 seconds. (5 seconds is the default, but it can be configured).
  3. See if the runnable has been run. If it has, go back to 1.
  4. If the runnable has not been run, which means that the UI thread has been blocked for at least 5 seconds, it raises an error with all running threads stack traces.

Usage

Install

Nuget

Install-Package Xamarin.ANRWatchDog

Reading the ANRError exception report

The ANRError stack trace is a bit particular, it has the stack traces of all the threads running in your application. So, in the report, each caused by section is not the cause of the precedent exception, but the stack trace of a different thread.

Here is a dead lock example:

FATAL EXCEPTION: |ANR-WatchDog|
    Process: anrwatchdog.github.com.testapp, PID: 26737
    com.github.anrwatchdog.ANRError: Application Not Responding
    Caused by: com.github.anrwatchdog.ANRError$_$_Thread: main (state = WAITING)
        at testapp.MainActivity$1.run(MainActivity.java:46)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
    Caused by: com.github.anrwatchdog.ANRError$_$_Thread: APP: Locker (state = TIMED_WAITING)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:1031)
        at java.lang.Thread.sleep(Thread.java:985)
        at testapp.MainActivity.SleepAMinute(MainActivity.java:18)
        at testapp.MainActivity.access$100(MainActivity.java:12)
        at testapp.MainActivity$LockerThread.run(MainActivity.java:36)

From this report, we can see that the stack traces of two threads. The first (the "main" thread) is stuck at MainActivity.java:46 while the second thread (named "App: Locker") is locked in a Sleep at MainActivity.java:18.
From there, if we looked at those two lines, we would surely understand the cause of the dead lock!

Note that some crash reporting library (such as Crashlytics) report all thread stack traces at the time of an uncaught exception. In that case, having all threads in the same exception can be cumbersome. In such cases, simply use setReportMainThreadOnly().

Configuration

Timeout (minimum hanging time for an ANR)

  • To set a different timeout (5000 millis is the default):

    if (BuildConfig.DEBUG == false) {
        new ANRWatchDog(10000 /*timeout*/).Start();
    }

Debugger

  • By default, the watchdog will ignore ANRs if the debugger is attached or if the app is waiting for the debugger to attach. This is because it detects execution pauses and breakpoints as ANRs. To disable this and throw an ANRError even if the debugger is connected, you can add setIgnoreDebugger(true):

    new ANRWatchDog().SetIgnoreDebugger(true).Start();

On ANR callback

  • If you would prefer not to crash the application when an ANR is detected, you can enable a callback instead:

    public class MyANRListener : ANRWatchDog.IANRListener
    {
      public void OnAppNotResponding(ANRError error)
      {
        //Handle the error.  For example, log it to HockeyApp:
        ExceptionHandler.SaveException(error, new CrashManager());
      }
    }
    
    new ANRWatchDog().SetANRListener(new MyANRListener()).Start();

    This is very important when delivering your app in production.

    • When in the hand of the final user, it's probably better not to crash after 5 seconds, but simply report the ANR to whatever reporting system you use. Maybe, after some more seconds, the app will "de-freeze".

Filtering reports

  • If you would like to have only your own threads to be reported in the ANRError, and not all threads (including system threads such as the FinalizerDaemon thread), you can set a prefix: only the threads whose name starts with this prefix will be reported.

    new ANRWatchDog().SetReportThreadNamePrefix("APP:").Start();

    Then, when you start a thread, don't forget to set its name to something that starts with this prefix (if you want it to be reported):

    public class MyAmazingThread : Thread {
        public override void Run() {
            NAME = "APP: Amazing!";
            /* ... do amazing things ... */
        }
    }
  • If you want to have only the main thread stack trace and not all the other threads, you can:

    new ANRWatchDog().SetReportMainThreadOnly().Start();

ANR Interceptor

  • Sometimes, you want to know that the application has froze for a certain duration, but not report the ANR error just yet.

  • You can define an interceptor that will be called before reporting an error.

  • The role of the interceptor that will be called before reporting an error.

  • The role of the interceptor is to define whether or not, given the given freeze duration, an ANR error should be raised or postponed.

public class MyANRInterceptor : IANRInterceptor
{
  public long Intercept(long duration)
  {
    long ret = 5000 - duration;
    if(ret > 0)
    {
      Log.Warn(TAG, $"Intercepted ANR that is too short ({duration} ms), postponing for {ret} ms");
    }
    return ret;
  }
}

new ANRWatchDog(2000).SetANRInterceptor(new MyANRInterceptor).Start();

Watchdog thread

  • ANRWatchDog is a thread, so you can interrupt it at any time.

  • If you are programming with Android's multi process capability (like starting an activity in a new process), remember that you will need an ANRWatchDog thread per process.

xamarin.anrwatchdog's People

Contributors

nwestfall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

awmoeder tbambuch

xamarin.anrwatchdog's Issues

ANRWatchDog ANRListener not working in debug mode

I'm trying to use this repo to get ANR errors and log it into Crashlytics but unfortunately while debugging niether call back for SetANRListener is working nor SetANRInterceptor.
Here is how i'm using it.

in my MainActivity

new ANRWatchDog(1000).SetANRListener(new MyANRListener())
                .SetIgnoreDebugger(true)
                .SetANRInterceptor(new MyANRInterceptor())
               // .SetReportMainThreadOnly()
                .Start();

Classes for listeners

public class MyANRListener : ANRWatchDog.IANRListener
	{
		public void OnAppNotResponding(ANRError error)
		{
            //Handle the ANR error. log it to Crashlytics:
            try
            {
                Crashlytics.Crashlytics.LogException(error);
            } catch(Exception ex)
            {

            }
		}
    }
public class MyANRInterceptor : IANRInterceptor
    {
        string TAG = "ANR: ";
        public long Intercept(long duration)
        {
            long ret = 5000 - duration;
            if (ret > 0)
            {
                Log.Warn(TAG, $"Intercepted ANR that is too short ({duration} ms), postponing for {ret} ms");
            }
            return ret;
        }
        public MyANRInterceptor()
        {
        }
    }

I'm trying with Thread.Sleep(10000); i'm getting ANR but none of your listener are getting hit while debugging.

System.ObjectDisposedException: Cannot access a disposed object.

Found this in a production app.

08-29 12:48:54.566 15566 15626 I MonoDroid: System.ObjectDisposedException: Cannot access a disposed object.
08-29 12:48:54.566 15566 15626 I MonoDroid: Object name: 'Xamarin.ANRWatchDog.ANRWatchDog'.
08-29 12:48:54.566 15566 15626 I MonoDroid:   at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00029] in <6b4d472d0e7640c0a2bb8ea6a688960b>:0 
08-29 12:48:54.566 15566 15626 I MonoDroid:   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualBooleanMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00000] in <6b4d472d0e7640c0a2bb8ea6a688960b>:0 
08-29 12:48:54.566 15566 15626 I MonoDroid:   at Java.Lang.Thread.get_IsInterrupted () [0x0000a] in <da577404d6634c2f9a27b8d89c12080f>:0 
08-29 12:48:54.566 15566 15626 I MonoDroid:   at Xamarin.ANRWatchDog.ANRWatchDog.Run () [0x000cd] in <1ed3a6a0bfdf423caf4a062aa180c1d5>:0 
08-29 12:48:54.566 15566 15626 I MonoDroid:   at Java.Lang.Thread.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00008] in <da577404d6634c2f9a27b8d89c12080f>:0 
08-29 12:48:54.566 15566 15626 I MonoDroid:   at (wrapper dynamic-method) System.Object.ef71051d-c506-4095-bfa9-683673b4b560(intptr,intptr)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.