如何调试在 Android 上消失而不抛出任何异常的 Xamarin Forms 应用程序?

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

编辑:我在 logcat 中发现了错误。请参阅下面的确认答案

我是 Xamarin 的新手,但已经完成了大量 Android 开发工作。我有一种情况,我的 Xamarin 应用程序正在消失,没有任何通知或异常被抛出。我似乎找不到如何调试这种情况。

我已将所有内容(令人作呕)包装在 try/catch 块中,但没有抛出任何异常(或者至少没有捕获异常)。

我实现了在这个博客的堆栈溢出答案中找到的全局错误处理例程。如果我故意抛出错误,全局错误处理程序会捕获它,因此它可以正常工作。但是,在这种情况下仍然没有例外。

我的应用程序在播放一段动画后崩溃了。通常是平移或捏合。如果我缩小并且显示的位图很大,它会更快崩溃。有一个停顿,然后应用程序消失了。

我的应用程序正在使用 SkiaSharp 制作大量位图动画。我在后台绘制位图 (Task.run( () => <My Drawing code>)),然后引发事件以使表面视图无效

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

绘制位图。当纵横比不变时(即不执行收缩),我将重复使用工作位图,并在创建新位图时处理旧位图。

为了限制绘图,我有一个接受绘图请求的队列。因此,为了绘制,我将一个绘制请求排入队列并在后台启动一个任务(参见上面的任务 运行)。当该任务启动时,它会从队列中请求绘制请求。队列将 return 不是下一个,而是最新的一个,并杀死任何其他较旧的请求。如果线程启动时没有请求,它会立即退出。如果用户进行大量的捏合或平移操作,这会限制绘制的位图数量。我正在锁定队列以确保线程不会越界。

我偶尔在输出 window 中注意到我会看到 #15 到 #19 之间的线程已经启动。虽然不一致。

是否有可能是启动了太多线程而 OS 正在终止应用程序?如果使用太多内存,OS 会终止应用程序吗?

我知道 Android 会在没有通知的情况下终止应用程序,但我从来没有让它在应用程序处于活动状态并且 运行ning 时这样做。我确定我的代码中有错误,但是当我没有得到任何关于正在发生的事情的反馈时,我不知道如何找到它。

有没有办法从 OS 中看出应用关闭的原因?有没有办法查看可能在 Xamarin Forms 应用程序的 OS 级别记录的错误?

编辑: 我一直在使用 adb logcat --pid=<my pid> 来监控该应用程序,但关闭时没有任何输出。我还没有尝试查看完整的 logcat 未过滤的日志,但是......但如果有任何内容,我会更新。

编辑#2 使用logcat,我发现了错误。它没有显示在 Visual Studios logcat 输出中,我不得不直接连接 adb logcat *:E 并观察所有错误。以下是相关位:

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[=15=]$SurfaceAnimator:100

这是我认为正在发生的事情。输入通道已终止,OS 正在终止应用程序。

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)

您不是在等待任务完成,也不是在其中捕获异常。在“即发即忘”的情况下,应用上下文永远不会捕获异常。

有两种选择。

等待任务将异常抛出堆栈:

 await Task.Run(() => < My Drawing code >);

或者在任务中捕获异常:

        await Task.Run(() =>
        {
            try
            {
              < My Drawing code >
            }
            catch (Exception ex)
            {
              < Log exception >
            }
        });

最终我找到了调试和解决问题的方法。

为了调试,正如@SushiHangover 所说,我使用了LogCat。但是,通常在使用 LogCat 时,我会按我的应用程序或进程 ID (pid) 进行过滤。在上面的场景中,OS 正在终止应用程序。因此,我的应用程序没有抛出任何异常,也没有记录任何信息。该应用程序就消失了。因此,过滤到我的应用程序的日志没有显示任何信息。我删除了过滤器,在很长一段时间查看 phone 上每个应用程序的所有日志消息后,我发现了潜在的问题。

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.

(请注意,PID 隐藏在消息的文本中,但进程的 PID 来自 OS。另请注意,应用程序名称不会出现在任何地方。因此要跟踪此信息,你必须在它崩溃之前知道你的 PID,然后在消息中对你的 pid 进行文本搜索)

根据这些信息,我可以看到 OS 正在毫无警告或异常地杀死我的应用程序,因为我处理触摸事件的时间太长了。我所做的所有渲染都是在后台线程上完成的,只有主要的绘制事件在主线程上完成。然而,我没有意识到我的后台线程并没有像我预期的那样将位图缩小到屏幕大小,而是返回了一个非常大的位图。然后,当应用程序何时在主线程上绘制时,它将这个非常大的位图压缩到屏幕大小。这个过程花费了很长时间,导致触摸事件被延迟。我修复了后端渲染,以便它在后台线程上执行收缩。这最终解决了问题,一切都像丝绸一样流畅,没有意外终止 OS。

要点:

  1. 如果您的应用程序在没有警告的情况下消失,您必须检查 OS 消息是否有错误。在这种情况下,您必须对您的 PID 进行文本搜索(即不要过滤您的 PID)
  2. 如果您收到 InputDispatcher 警告或错误,您可能在主线程中消耗了太多处理时间。