在 Xamarin 上调试 Java.Lang.RuntimeException

Debugging Java.Lang.RuntimeException on Xamarin

自 2 月下旬以来,我发布的游戏就因为这个问题 java.exception 而不断崩溃。 大约 10 分钟后(根据我的经验,顺便说一句,这真的很正常,从 1 分钟到 30 分钟甚至 60 分钟,甚至从未发生过)游戏崩溃了。 即使玩了 2 小时后,我也无法通过连接的调试器重现崩溃。

我正在使用 Hockeyapp 报告应用程序崩溃,但堆栈跟踪绝对是空的:

Package: hb.doom_and_destiny_advanced
Version: 1516
Android: 5.1.1
Manufacturer: HUAWEI
Model: HUAWEI SCL-L01
Date: 24/06/2016 22:25:51

Java.Lang.RuntimeException: Only the original thread that created a view hierarchy can touch its views.

似乎没有明显的失败原因,而且游戏没有使用 UI 系统,如果不是在 Monogame 框架下的话。 经过几个月的测试,我真的忍不住请求一些帮助来调试这个严重让我发疯的错误。

目前主要问题是无法在一致且可行的测试时间内重现崩溃: 它可以在 1 分钟或超过 60 分钟内发生,并且它似乎在某种程度上受用户交互的影响(例如,在 运行 基准测试时我从来没有看到错误)。

检查 android 日志也无济于事,因为它们包含来自许多 android 系统的极其混乱的信息,但是,就单声道而言,它一点也不冗长。

06-20 19:04:16.917: W/ls(27999): type=1400 audit(0.0:3522): avc: denied { getattr } for path="/init" dev="rootfs" ino=5443 scontext=u:r:shell:s0 tcontext=u:object_r:init_exec:s0 tclass=file permissive=0
06-20 19:04:16.917: W/ls(27999): type=1400 audit(0.0:3523): avc: denied { getattr } for path="/persist" dev="mmcblk0p16" ino=2 scontext=u:r:shell:s0 tcontext=u:object_r:persist_file:s0 tclass=dir permissive=0
06-20 19:04:18.068: D/ConnectivityService(772): notifyType CAP_CHANGED for NetworkAgentInfo [WIFI () - 101]
06-20 19:04:19.215: W/SoundPool(12501):   sample 1 not READY
06-20 19:04:19.254: I/OMXClient(12501): Using client-side OMX mux.
06-20 19:04:19.258: E/OMXMaster(12501): A component of name 'OMX.qcom.audio.decoder.aac' already exists, ignoring this one.
06-20 19:04:19.418: W/AudioTrack(12501): AUDIO_OUTPUT_FLAG_FAST denied by client; transfer 4, track 44100 Hz, output 48000 Hz
06-20 19:04:19.419: D/audio_hw_primary(198): out_set_parameters: enter: usecase(1: low-latency-playback) kvpairs: routing=2
06-20 19:04:19.430: D/audio_hw_primary(198): select_devices: out_snd_device(2: speaker) in_snd_device(0: none)
06-20 19:04:19.430: D/msm8974_platform(198): platform_send_audio_calibration: sending audio calibration for snd_device(2) acdb_id(15)
06-20 19:04:19.430: D/audio_hw_primary(198): enable_audio_route: apply and update mixer path: low-latency-playback
06-20 19:04:19.608: I/art(12501): Starting a blocking GC Explicit
06-20 19:04:19.624: I/art(12501): Explicit concurrent mark sweep GC freed 3391(130KB) AllocSpace objects, 3(604KB) LOS objects, 25% free, 46MB/62MB, paused 265us total 15.711ms
06-20 19:04:21.082: D/ConnectivityService(772): notifyType CAP_CHANGED for NetworkAgentInfo [WIFI () - 101]
06-20 19:04:21.941: D/audio_hw_primary(198): out_set_parameters: enter: usecase(0: deep-buffer-playback) kvpairs: routing=2
06-20 19:04:21.927: W/ls(28029): type=1400 audit(0.0:3524): avc: denied { getattr } for path="/init" dev="rootfs" ino=5443 scontext=u:r:shell:s0 tcontext=u:object_r:init_exec:s0 tclass=file permissive=0
06-20 19:04:21.927: W/ls(28029): type=1400 audit(0.0:3525): avc: denied { getattr } for path="/persist" dev="mmcblk0p16" ino=2 scontext=u:r:shell:s0 tcontext=u:object_r:persist_file:s0 tclass=dir permissive=0
06-20 19:04:22.278: W/SoundPool(12501):   sample 2 not READY
06-20 19:04:22.292: I/OMXClient(12501): Using client-side OMX mux.
06-20 19:04:22.296: E/OMXMaster(12501): A component of name 'OMX.qcom.audio.decoder.aac' already exists, ignoring this one.
06-20 19:04:22.479: W/AudioTrack(12501): AUDIO_OUTPUT_FLAG_FAST denied by client; transfer 4, track 44100 Hz, output 48000 Hz
06-20 19:04:22.490: D/audio_hw_primary(198): out_set_parameters: enter: usecase(1: low-latency-playback) kvpairs: routing=2
06-20 19:04:22.527: D/AudioFlinger(198): mixer(0xb41c0000) throttle end: throttle time(4)
06-20 19:04:22.941: D/audio_hw_primary(198): out_set_parameters: enter: usecase(0: deep-buffer-playback) kvpairs: routing=2
06-20 19:04:25.784: D/audio_hw_primary(198): disable_audio_route: reset and update mixer path: low-latency-playback
06-20 19:04:26.927: W/ls(28070): type=1400 audit(0.0:3526): avc: denied { getattr } for path="/init" dev="rootfs" ino=5443 scontext=u:r:shell:s0 tcontext=u:object_r:init_exec:s0 tclass=file permissive=0
06-20 19:04:26.927: W/ls(28070): type=1400 audit(0.0:3527): avc: denied { getattr } for path="/persist" dev="mmcblk0p16" ino=2 scontext=u:r:shell:s0 tcontext=u:object_r:persist_file:s0 tclass=dir permissive=0
06-20 19:04:27.114: D/ConnectivityService(772): notifyType CAP_CHANGED for NetworkAgentInfo [WIFI () - 101]
06-20 19:04:28.947: I/art(12501): Starting a blocking GC Explicit
06-20 19:04:28.960: I/art(12501): Explicit concurrent mark sweep GC freed 2256(88KB) AllocSpace objects, 0(0B) LOS objects, 25% free, 46MB/62MB, paused 285us total 12.462ms
06-20 19:04:28.984: I/mono-stdout(12501): Writing unhandled exception to: /data/user/0/hb.doom_and_destiny_advanced/files/77b60833-c767-4692-9068-4b829619d394.stacktrace

该游戏确实大量使用网络调用,所以我在想如果连接不一致,可能 android 会尝试显示弹出窗口。由于可能会从辅助 task/thread 中调用弹出窗口,因此它很可能会因为 Xamarin 的单声道实现中缺少 "RunOnUiThread" 而崩溃。

如果是这种情况,我可以通过发布旧版本的 Xamarin Android 来检查它,但是 Gen 2016 版本不再可供下载。

将异常记录软件更新到最新版本后,错误日志似乎提供了更多信息,我仍然不知道哪个线程崩溃了,但我肯定在搜索中得到了更多的点击。

Xamarin 原因:android.runtime.JavaProxyThrowable:System.AggregateException:通过等待任务或访问其异常 属性 未观察到任务的异常。结果,未观察到的异常被终结器线程重新抛出。 ---> Java.Lang.RuntimeException: 只有创建视图层次结构的原始线程才能触及它的视图。 --- 内部异常堆栈跟踪结束 --- --->(内部异常 #0)Java.Lang.RuntimeException:只有创建视图层次结构的原始线程才能触及它的视图。<---

at dalvik.system.NativeStart.run(Native Method)

似乎有 2 个问题的组合:

1) 任务崩溃,可能是因为

(Inner Exception #0) Java.Lang.RuntimeException: Only the original thread that created a view hierarchy can touch its views.<---

2) 一旦 gc 收集任务,异常就会重新抛出并用外部

杀死应用程序
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property.

正如我在最新发现中所建议的那样,崩溃是由于任务抛出异常,并等待垃圾收集器完成以重新抛出异常。

这更难调试,因为我没有得到完整的异常层次结构,因为我在崩溃报告软件中使用了修改后的异常处理程序,它只提取了最新的 "InnerException"。

实际的异常是我试图从一个不是视图创建线程的线程禁用自动屏幕变暗。

看到这 2 个步骤并添加到我的代码库

  1. 从所有的 oncreate 调用此方法 HandleGlobalExceptions() 您的活动或
  2. 创建一个基础 activity 并覆盖 oncreate of base activity 并调用这个方法 要添加的示例代码

    使用 ar = Android.Runtime;

        public static void HandleGlobalExceptions()
            {
                AppDomain currentDomain = AppDomain.CurrentDomain;
                currentDomain.UnhandledException += HanldleDomainGlobalException;
                ar.AndroidEnvironment.UnhandledExceptionRaiser += HandleAndroidEnvException;
            }
    

    这2个帮手叫

            private static void HandleAndroidEnvException(object sender, ar.RaiseThrowableEventArgs e)
            {
                Log.Wtf("EnvGlobal", e.Exception.ToString());
     // display or handle error here , the way uwant           
            }
    
            private static void HanldleDomainGlobalException(object sender, UnhandledExceptionEventArgs e)
            {
                Log.Wtf("DomainGlobal", e?.ExceptionObject?.ToString());
     // display or handle error here , the way uwant
            }