请选择与 Visual Studio 2017 安装相对应的正确 System.ValueTuple NuGet。
GC 消息
将 debug.mono.log 系统属性设置为包含 gc 的值,即可查看 GC 组件消息。
每当 GC 执行并提供有关 GC 执行了多少工作的信息时,系统生成 GC 消息:
I/monodroid-gc(12331): GC cleanup summary: 81 objects tested - resurrecting 21.
将 MONO_LOG_LEVEL
环境变量设置为 debug
,即可生成其他 GC 信息(例如计时信息):
adb shell setprop debug.mono.env MONO_LOG_LEVEL=debug
这将产生(大量)额外的 Mono 消息,包括以下三个结果:
D/Mono (15723): GC_BRIDGE num-objects 1 num_hash_entries 81226 sccs size 81223 init 0.00ms df1 285.36ms sort 38.56ms dfs2 50.04ms setup-cb 9.95ms free-data 106.54ms user-cb 20.12ms clenanup 0.05ms links 5523436/5523436/5523096/1 dfs passes 1104 6883/11046605
D/Mono (15723): GC_MINOR: (Nursery full) pause 2.01ms, total 287.45ms, bridge 225.60 promoted 0K major 325184K los 1816K
D/Mono ( 2073): GC_MAJOR: (user request) pause 2.17ms, total 2.47ms, bridge 28.77 major 576K/576K los 0K/16K
在 GC_BRIDGE
消息中,num-objects
是此次传递考虑使用的桥对象数,num_hash_entries
是此次桥代码调用期间处理的对象数。
在 GC_MINOR
和 GC_MAJOR
消息中,total
是环境暂停的时间量(没有线程正在执行),而 bridge
是桥处理代码(处理 Java VM)所用的时间量。 发生桥处理时,环境未暂停。
通常,num_hash_entries
的值越大,bridge
集合花费的时间就越多,收集所花费的 total
时间就越多。
全局引用消息
若要启用全局引用 (GREF) 日志记录,debug.mono.log 系统属性必须包含 gref,例如:
adb shell setprop debug.mono.log gref
Xamarin.Android 使用 Android 全局引用来提供 Java 实例和相关托管实例之间的映射,就像调用 Java 方法时,需要向 Java 提供 Java 实例一样。
遗憾的是,Android 仿真器一次只允许使用 2000 个全局引用。 硬件具有的全局引用限制高得多,是 52000 个。 在仿真器上运行应用程序时,限制较低会出现问题,因此知道实例的来源非常有用。
全局引用计数限于 Xamarin.Android 内部,不包括(也不能包括)由加载到进程中的其他本机库提取出的全局引用。 将全局引用计数用作估计值。
I/monodroid-gref(12405): +g+ grefc 108 gwrefc 0 obj-handle 0x40517468/L -> new-handle 0x40517468/L from at Java.Lang.Object.RegisterInstance(IJavaObject instance, IntPtr value, JniHandleOwnership transfer)
I/monodroid-gref(12405): at Java.Lang.Object.SetHandle(IntPtr value, JniHandleOwnership transfer)
I/monodroid-gref(12405): at Java.Lang.Object..ctor(IntPtr handle, JniHandleOwnership transfer)
I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler, Boolean removable)
I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler)
I/monodroid-gref(12405): at Android.App.Activity.RunOnUiThread(System.Action action)
I/monodroid-gref(12405): at Mono.Samples.Hello.HelloActivity.UseLotsOfMemory(Android.Widget.TextView textview)
I/monodroid-gref(12405): at Mono.Samples.Hello.HelloActivity.<OnCreate>m__3(System.Object o)
I/monodroid-gref(12405): handle 0x40517468; key_handle 0x40517468: Java Type: `mono/java/lang/RunnableImplementor`; MCW type: `Java.Lang.Thread+RunnableImplementor`
I/monodroid-gref(12405): Disposing handle 0x40517468
I/monodroid-gref(12405): -g- grefc 107 gwrefc 0 handle 0x40517468/L from at Java.Lang.Object.Dispose(System.Object instance, IntPtr handle, IntPtr key_handle, JObjectRefType handle_type)
I/monodroid-gref(12405): at Java.Lang.Object.Dispose()
I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor.Run()
I/monodroid-gref(12405): at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
I/monodroid-gref(12405): at System.Object.c200fe6f-ac33-441b-a3a0-47659e3f6750(IntPtr , IntPtr )
I/monodroid-gref(27679): +w+ grefc 1916 gwrefc 296 obj-handle 0x406b2b98/G -> new-handle 0xde68f4bf/W from take_weak_global_ref_jni
I/monodroid-gref(27679): -w- grefc 1915 gwrefc 294 handle 0xde691aaf/W from take_global_ref_jni
有四个重要的消息:
全局引用创建:这些消息行以 +g+ 开头,将为创建代码路径提供堆栈跟踪。
全局引用析构:这些消息行以 -g- 开头,可能为全局引用的代码路径处理提供堆栈跟踪。 如果 GC 正在处理 gref,则不会提供堆栈跟踪。
弱全局引用创建:这些行以 +w+ 开头。
弱全局引用析构:这些行以 -w- 开头。
在所有消息中,grefc 值是 Xamarin.Android 创建的全局引用计数,grefwc 值是 Xamarin.Android 创建的弱全局引用计数。 而 handle 或 obj-handle 值是 JNI 句柄值,“”后面的字符是句柄值的类型:/L 表示本地引用,/G 表示全局引用,/W 表示弱全局引用/。
在 GC 过程中,系统将全局引用 (+g+) 转换为弱全局引用(得到 +w+ 和 -g-),逐出 Java 端的 GC,然后检查是否收集了弱全局引用。 如果仍然存在,则围绕弱引用(+g+、-w-)创建一个新的 gref,否则弱引用将被销毁 (-w)。
Java 实例由 MCW 创建和包装
I/monodroid-gref(27679): +g+ grefc 2211 gwrefc 0 obj-handle 0x4066df10/L -> new-handle 0x4066df10/L from ...
I/monodroid-gref(27679): handle 0x4066df10; key_handle 0x4066df10: Java Type: `android/graphics/drawable/TransitionDrawable`; MCW type: `Android.Graphics.Drawables.TransitionDrawable`
I/monodroid-gref(27679): +w+ grefc 1953 gwrefc 259 obj-handle 0x4066df10/G -> new-handle 0xde68f95f/W from take_weak_global_ref_jni
I/monodroid-gref(27679): -g- grefc 1952 gwrefc 259 handle 0x4066df10/G from take_weak_global_ref_jni
对象仍存在,因为句柄 != null
wref 恢复为 gref
I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x4066df10
I/monodroid-gref(27679): +g+ grefc 1930 gwrefc 39 obj-handle 0xde68f95f/W -> new-handle 0x4066df10/G from take_global_ref_jni
I/monodroid-gref(27679): -w- grefc 1930 gwrefc 38 handle 0xde68f95f/W from take_global_ref_jni
对象不存在,因为句柄 == null
wref 已释放,未创建任何新 gref
I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x0
I/monodroid-gref(27679): -w- grefc 1914 gwrefc 296 handle 0xde68f95f/W from take_global_ref_jni
有一个有趣的窍门:在运行 Android 4.0 之前版本的目标上,gref 值等于 Java 对象在 Android 运行时内存中的地址。 (也就是说,GC 是一个不移动、保守、收集器,它正在分发对这些对象的直接引用。因此,在 +g+、+w+、-g-、-w- 序列之后,生成的 gref 将具有与原始 gref 值相同的值。 这使检索整个日志变得非常简单。
不过,Android 4.0 具有移动的收集器,不再提供对 Android 运行时 VM 对象的直接引用。 于是,在 a+g+、+w+、-g-、+g+、-w- 序列之后,gref 值将变得不同。 如果对象在经过多次 GC 后仍然存在,它将有几个 gref 值,这就很难确定分配实例的实际位置。
以编程方式查询
可以通过查询 JniRuntime
对象来查询 GREF 和 WREF 计数。
Java.Interop.JniRuntime.CurrentRuntime.GlobalReferenceCount
- 全局引用计数
Java.Interop.JniRuntime.CurrentRuntime.WeakGlobalReferenceCount
- 弱引用计数
Android 调试日志
Android 调试日志可能会提供有关你看到的任何运行时错误的其他上下文。
或者说,“我的应用在调试版本中的运行速度比发布版本快 10 倍!”
Xamarin.Android 支持多个设备 ABI:armeabi、armeabi-v7a 和 x86。 可以在“项目属性>应用程序”选项卡>支持的体系结构中指定设备 ABIS。
调试版本使用提供所有 ABI 的 Android 包,因此将为目标设备使用最快的 ABI。
发布版本将仅包括“项目属性”选项卡中选择的 API。可以选择多个。
“armeabi”是默认的 ABI,具有最广泛的设备支持。 然而,armeabi 不支持多 CPU 设备和硬件浮点等。 因此,使用 armeabi 发布运行时的应用将与单个核心绑定,并将使用软浮动实现。 这两种情况都会显著降低应用的性能。
如果应用需要良好的浮点性能(例如游戏),你应该启用 armeabi-v7a ABI。 你可能只想支持 armeabi-v7a 运行时,尽管这意味着只支持 armeabi 的旧设备将无法运行你的应用。
找不到 Android SDK
Google 提供了 2 个适用于 Windows 的 Android SDK 下载。
如果选择 .exe 安装程序,它将编写注册表项,告诉 Xamarin.Android 安装位置。 如果选择 .zip 文件并自行解压缩,Xamarin.Android 不知道在何处查找 SDK。 可以通过转到“工具>选项 > Xamarin Android”设置,告知 Xamarin.Android > 在 Visual Studio 中 SDK 的位置:
IDE 不显示目标设备
有时,你会尝试将应用程序部署到设备,但要部署到的设备未显示在“选择设备”对话框中。 Android Debug Bridge 出现异常时,可能会发生这种情况。
若要诊断此问题,请找到 adb 程序,然后运行:
adb devices
如果设备不存在,则需要重启 Android Debug Bridge 服务器,以便可以找到设备:
adb kill-server
adb start-server
HTC 同步软件可能会阻止“adb start-server”正常运行。 如果“adb start-server”命令没有打印出它启动所用的端口,请退出 HTC 同步软件并尝试重启 adb 服务器。
这意味着,路径不包含 Java SDK 的 bin 目录所在的目录。 检查是否遵循了安装指南中的步骤。
monodroid.exe 或 aresgen.exe 已退出,显示错误代码 1
若要帮助调试此问题,请转到 Visual Studio 并更改 MSBuild 详细级别,为此,请选择: 工具 > 选项 > 项目 和 解决方案 > 生成 并 运行 > MSBuild 项目生成输出详细程度 ,并将此值设置为 Normal。
重新生成,并检查 Visual Studio 的输出窗格,该窗格应包含完整错误。
设备上没有足够的存储空间用于部署包
如果不从 Visual Studio 内启动仿真器,就会发生这种情况。 在 Visual Studio 之外启动仿真器时,需要传递 -partition-size 512
选项,例如
emulator -partition-size 512 -avd MonoDroid
确保使用正确的仿真器名,即配置仿真器时使用的名称。
安装包时INSTALL_FAILED_INVALID_APK
Android 包名称必须包含句点(“.”)。 编辑包名称,使其包含句点。
在 Visual Studio 中: