Search code examples
androidxamarinxamarin.formsxamarin.androidskiasharp

How to debug a Xamarin Forms app that disappears on Android without any exception being thrown?


EDIT: I found the error in logcat. See the confirmed answer below

I'm new to Xamarin but have done a fair amount of straight Android development. I have a situation where my Xamarin app is disappearing without any notification or exception being thrown. I can't seem to find how to debug this situation.

I have wrapped everything (to ad nauseum) in try/catch blocks but no exceptions are thrown (or at least they aren't caught).

I implemented the global error handling routines found in this stack overflow answer which comes from this blog. If I throw an error purposely, the global error handler catches it, so it is functional. But still, no exception is caught in this scenario.

My app crashes after a bit of animation. Typically panning or pinching. It crashes quicker if I am zoomed out and the bitmap being displayed is large. There is a pause, and then the app disappears.

My app is doing a lot of bitmap animations with SkiaSharp. I draw bitmaps in the background (Task.run( () => <My Drawing code>)) and then raise an event to invalidate the surface view

MainThread.BeginInvokeOnMainThread(() =>
    {
        // Code to run on the main thread
        canvasView.InvalidateSurface();
    }); 

to draw the bitmap. I'm reusing work bitmaps when aspect ratios don't change (i.e. no pinch performed) and I dispose of the old bitmaps when a new one is created.

To limit the drawing, I have a queue that takes draw requests. So to draw, I enqueue a draw request and fire off a task in the background (see task run above). When that tasks starts up, it asks for a draw request from the queue. The queue will return not the next one, but the newest one and kill any other request that is older. If there are no requests when a thread starts up, it exits immediately. This limits the number of bitmaps being drawn if the user is doing a lot of pinching or panning. I am locking on the queue to ensure the threads aren't overstepping.

I have occasionally noticed in the output window that I will see between #15 to #19 threads having been started. It's not consistent though.

Is it possible that too many threads are being fired up and the OS is killing the app? Will the OS kill the app if too much memory is used?

I know Android will kill an app without notification, but I have never had it do it while an app was active and running. I am sure there is an error in my code, but I don't know how to find it when I get nothing back about what is going on.

Is there a way to see from the OS why an app was closed? Is there a way to see errors that might get logged at the OS level for a Xamarin Forms app?

EDIT: I have been using adb logcat --pid=<my pid> to monitor the app but nothing is ever output when it's closed. I have not tried looking at the full logcat unfiltered logs, yet...but I will and will update if anything is there.

EDIT #2 Using logcat, I found the error. It doesn't show in Visual Studios logcat output, I had to connect directly with adb logcat *:E and watch all the errors. Here are the relative bits:

07-30 23:25:48.796 1324 1480 E WindowManager: win=Window{491bfd6 u0 Splash Screen com.valleystreams.dupler EXITING} destroySurfaces: appStopped=false win.mWindowRemovalAllowed=true win.mRemoveOnExit=true win.mViewVisibility=0 caller=com.android.server.wm.AppWindowToken.destroySurfaces:1178 com.android.server.wm.AppWindowToken.destroySurfaces:1159 com.android.server.wm.WindowState.onExitAnimationDone:5055 com.android.server.wm.WindowStateAnimator.onAnimationFinished:284 com.android.server.wm.WindowState.onAnimationFinished:5507 com.android.server.wm.-$$Lambda$yVRF8YoeNdTa8GR1wDStVsHu8xM.run:2 com.android.server.wm.SurfaceAnimator.lambda$getFinishedCallback$0$SurfaceAnimator:100

Here is the what I believe is happening. The input channel has died and the OS is killing the app.

07-30 23:28:07.285 1324 1749 E InputDispatcher: channel 'fc5d006 com.valleystreams.dupler/crc646da0ed5db1bd092c.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!

07-30 23:28:07.384 1324 1479 E WindowManager: RemoteException occurs on reporting focusChanged, w=Window{fc5d006 u0 com.valleystreams.dupler/crc646da0ed5db1bd092c.MainActivity}

07-30 23:28:07.384 1324 1479 E WindowManager: android.os.DeadObjectException

07-30 23:28:07.384 1324 1479 E WindowManager: at android.os.BinderProxy.transactNative(Native Method)

07-30 23:28:07.384 1324 1479 E WindowManager: at android.os.BinderProxy.transact(BinderProxy.java:527)

07-30 23:28:07.384 1324 1479 E WindowManager: at android.view.IWindow$Stub$Proxy.windowFocusChanged(IWindow.java:829)

07-30 23:28:07.384 1324 1479 E WindowManager: at com.android.server.wm.WindowState.reportFocusChangedSerialized(WindowState.java:3654)

07-30 23:28:07.384 1324 1479 E WindowManager: at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:5255)

07-30 23:28:07.384 1324 1479 E WindowManager: at android.os.Handler.dispatchMessage(Handler.java:107)

07-30 23:28:07.384 1324 1479 E WindowManager: at android.os.Looper.loop(Looper.java:237)

07-30 23:28:07.384 1324 1479 E WindowManager: at android.os.HandlerThread.run(HandlerThread.java:67)

07-30 23:28:07.384 1324 1479 E WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:44)


Solution

  • Ultimately I found how to debug and resolve the issue.

    For debugging, as @SushiHangover said, I used LogCat. However, typically when using LogCat I filter by my application or by process id (pid). In the above scenario, the OS was killing the application. As such, my application was not throwing any exceptions and was not logging any information. The app simply disappears. So, the logs that are filtered to my application were showing no information. I removed the filters and after a very long time looking through all the log messages from every application on the phone, I found the underlying issue.

    08-01 01:21:04.155 1324 1749 D InputDispatcher: Waiting for application to become ready for input (8831): 706303f Reason: Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 8. Wait queue head age: 733.6ms.

    (note that the PID is buried in the text of the message but the PID of the process is from the OS. Also note that the application name doesn't appear anywhere. So to track this, you have to know your PID before it crashes, and then do a text search for your pid in the messages)

    With this information, I could see that the OS was killing my app without warning or exception because I was taking too long to process touch events. All of the rendering I was doing is done on a background thread, with only the main paint event being done on the main thread. However, what I didn't realize is my background thread was not reducing down the bitmap to the screen size like I intended but was returning a very large bitmap. Then, as the application when to paint on the main thread, it took this very large bitmap and squeezed it down to the screen size. This process was what was taking so long and causing the touch events to be delayed. I fixed the backend rendering so that it performed the shrink on the background thread. This ultimately solved the problem and everything is animating smooth as silk with no unexpected terminations from the OS.

    Takeaways:

    1. If your app disappears without warning, you have to check the OS messages for errors. And in this scenario, you have to do a text search for your PID (i.e. don't filter on your PID)
    2. If you are receiving InputDispatcher warnings or errors, you are probably eating too much process time in your main thread.