Xamarin Android: ошибка и исправление

Автор:

learn.microsoft.com

Получение диагностической информации

При отслеживании ошибок в Xamarin.Android есть несколько областей, к которым следует обращаться. К ним относятся:

  • Диагностический вывод MSBuild.
  • Журналы развертывания устройства.
  • Выходные данные журнала отладки Android.

Выходные данные диагностики MSBuild

Диагностический MSBuild может содержать дополнительную информацию о сборке пакетов и информацию о развертывании пакетов.

Чтобы включить диагностический вывод MSBuild в Visual Studio:

Нажмите Tools > Options...

  • В левой части древовидного представления выберите Projects and Solutions > Build and Run
  • В правой панели установите в раскрывающемся списке MSBuild build output verbosity значение Diagnostic.
  • Нажмите кнопку OK
  • Очистите и пересоберите пакет.
  • Диагностический вывод будет виден на панели Output.

Чтобы включить диагностический вывод MSBuild в Visual Studio для Mac/OS X:

  • Щелкните Visual Studio for Mac > Preferences...
  • В левой части древовидного представления выберите Projects > Build
  • В правой панели установите в раскрывающемся списке Log verbosity значение Diagnostic.
  • Нажмите кнопку OK
  • Перезапустите Visual Studio for Mac
  • Очистите и пересоберите пакет.
  • Диагностический вывод можно увидеть в панели Errors Pad (View > Pads > Errors ), нажав кнопку Build Output.

Журналы развертывания устройств

Чтобы включить ведение журнала развертывания устройств в Visual Studio, выполните следующие действия:

  • Tools > Options...
  • В левой части древовидного представления выберите Xamarin > Android Settings
  • В правой панели установите флажок [X] extension debug logging (writes monodroid.log to your desktop).
  • Сообщения журнала будут записываться в файл monodroid.log на рабочем столе.

Visual Studio для Mac всегда пишет журналы развертывания устройств. Найти их несколько сложнее: например, файл журнала AndroidUtils создается для каждого дня с указанием времени, когда происходит развертывание: AndroidTools-2012-10-24_12-35-45.log.

  • В Windows файлы журнала записываются в %LOCALAPPDATA%\XamarinStudio-{VERSION}\Logs.
  • В OS X файлы журнала записываются в $HOME/Library/Logs/XamarinStudio-{VERSION}.

Вывод отладочного журнала Android

Android записывает множество сообщений в отладочный журнал Android. Xamarin.Android использует системные свойства Android для управления генерацией дополнительных сообщений в отладочный журнал Android. Системные свойства Android можно задать с помощью команды setprop в Android Debug Bridge (adb):

adb shell setprop PROPERTY_NAME PROPERTY_VALUE

Системные свойства считываются при запуске процесса, поэтому их необходимо либо установить до запуска приложения, либо перезапустить приложение после изменения системных свойств.

Системные свойства Xamarin.Android

Xamarin.Android поддерживает следующие системные свойства:

  • debug.mono.debug: Если строка непустая, то это эквивалентно *mono-debug*.
  • debug.mono.env: Разделенный трубкой ('|') список переменных окружения для экспорта во время запуска приложения, до инициализации mono. Это позволяет задать переменные окружения, управляющие протоколированием mono.

Примечание. Поскольку значение разделено "|", оно должно иметь дополнительный уровень кавычек, так как команда "adb shell" удалит набор кавычек.

Примечание. Длина значений системных свойств Android не может превышать 92 символов.

Пример:

adb shell setprop debug.mono.env "'MONO_LOG_LEVEL=info|MONO_LOG_MASK=asm'"
  • debug.mono.log: Разделенный запятыми (',') список компонентов, которые должны выводить дополнительные сообщения в журнал отладки Android. По умолчанию ничего не задано. Компоненты включают:
  • - all: Печатать все сообщения
  • - gc: Выводить сообщения, связанные с GC.
  • - gref: Вывод сообщений о выделении и снятии ссылок (слабых, глобальных).
  • - lref: Вывод сообщений о выделении и снятии локальных ссылок.

Примечание. Эти сообщения очень многословны. Не включайте их, если это действительно необходимо.

  • debug.mono.trace: Позволяет установить параметр mono --trace=PROPERTY_VALUE.

Удаление bin и obj

В прошлом Xamarin.Android возникали следующие ошибки:

  • странная ошибка сборки или выполнения.
  • вы выполняете операции Clean, Rebuild или вручную удаляете каталоги bin и obj.
  • проблема исчезает.

Мы уделяем большое внимание устранению подобных проблем, поскольку они влияют на производительность разработчиков.

Если с вами случилась подобная проблема:

  • Сделайте мысленную заметку. Какое последнее действие привело ваш проект в такое состояние?
  • Сохраните текущий журнал сборки. Попробуйте выполнить сборку еще раз и запишите диагностический журнал сборки.
  • Отправьте отчет об ошибке.

Перед удалением каталогов bin и obj заархивируйте их и сохраните для последующей диагностики, если потребуется. Вероятно, для восстановления работоспособности можно просто выполнять операцию Clean приложения Xamarin.Android.

Xamarin.Android не может разрешить System.ValueTuple

Эта ошибка возникает из-за несовместимости с Visual Studio.

  • Visual Studio 2017 Update 1 (версия 15.1 или старше) совместима только с System.ValueTuple NuGet 4.3.0 (или старше).
  • Visual Studio 2017 Update 2 (версия 15.2 или новее) совместима только с System.ValueTuple NuGet 4.3.1 (или новее).

Пожалуйста, выберите правильный System.ValueTuple NuGet, соответствующий вашей установке Visual Studio 2017.

Сообщения GC

Сообщения компонента GC можно просмотреть, установив для системного свойства debug.mono.log значение, содержащее gc.

Сообщения GC генерируются при каждом выполнении GC и предоставляют информацию о том, какой объем работы выполнил GC:

I/monodroid-gc(12331): GC cleanup summary: 81 objects tested - resurrecting 21.

Дополнительная GC-информация, например, временная информация, может быть получена при установке переменной окружения MONO_LOG_LEVEL в значение debug:

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, тем больше времени займет сбор моста, и тем больше будет общее время сбора.

Cообщения по глобальным ссылкам

Для включения протоколирования Global Reference loggig (GREF) системное свойство debug.mono.log должно содержать gref, например:

adb shell setprop debug.mono.log gref

Xamarin.Android использует глобальные ссылки Android для обеспечения сопоставления между экземплярами 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.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-), запускается GC на стороне Java, а затем слабая глобальная ссылка проверяется на предмет того, была ли она собрана. Если она еще жива, то вокруг слабой ссылки создается новый gref (+g+, -w-), в противном случае слабая ссылка уничтожается (-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`

GC выполняется...

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

Объект по-прежнему активен, так как handle! = 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

Объект дезактивируется, так как handle == 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-, +g+, -w- результирующий gref будет иметь то же значение, что и исходный gref. Это делает поиск по логам достаточно простым.

Однако в Android 4.0 появился перемещаемый коллектор, который больше не выдает прямых ссылок на объекты ВМ среды исполнения Android. Следовательно, после последовательности +g+, +w+, -g-, +g+, -w- значение gref будет другим. Если объект переживет несколько GC, то он будет иметь несколько значений gref, что затрудняет определение того, откуда был выделен экземпляр.

Программное выполнение запросов

С помощью запроса к объекту JniRuntime можно узнать количество GREF и WREF.

Java.Interop.JniRuntime.CurrentRuntime.GlobalReferenceCount - Global Reference Count
Java.Interop.JniRuntime.CurrentRuntime.WeakGlobalReferenceCount - Weak Reference Count

Журналы отладки Android

Отладочные журналы Android могут предоставить дополнительную информацию о возникающих ошибках во время выполнения программы.

Производительность с плавающей точкой очень низкая!

Альтернативный вариант: «Мое приложение работает в 10 раз быстрее с отладочной сборкой, чем с релизной!».

Xamarin.Android поддерживает несколько ABI устройств: armeabi, armeabi-v7a и x86. ABI устройств можно указать в Свойствах проекта > вкладка Приложения > Поддерживаемые архитектуры.

В отладочных сборках используется пакет Android, содержащий все ABI, и, таким образом, будет использоваться самый быстрый ABI для целевого устройства.

Релизные сборки будут включать только те ABI, которые выбраны на вкладке Project Properties. Можно выбрать несколько.

armeabi - это ABI по умолчанию, имеющий наиболее широкую поддержку устройств. Однако armeabi не поддерживает многопроцессорные устройства и аппаратную плавающую точку, а также другие возможности. Следовательно, приложения, использующие среду исполнения armeabi Release, будут привязаны к одному ядру и будут использовать реализацию soft-float. Оба этих фактора могут привести к значительному снижению производительности приложения.

Если ваше приложение требует достойной производительности с плавающей точкой (например, игры), то вам следует включить armeabi-v7a ABI. Возможно, вы захотите поддерживать только среду выполнения armeabi-v7a, однако это означает, что старые устройства, поддерживающие только armeabi, не смогут запустить ваше приложение.

Не удалось найти Android SDK

Для Android SDK для Windows доступны две загрузки с сайта Google. Если вы выберете программу установки .exe, то она запишет ключи реестра, которые укажут Xamarin.Android, куда она была установлена. Если выбрать .zip-файл и распаковать его самостоятельно, Xamarin.Android не будет знать, где искать SDK. Вы можете указать Xamarin.Android, где находится SDK, в Visual Studio, перейдя в меню Tools > Options > Xamarin > Android Settings:

IDE не отображает целевое устройство

Иногда вы пытаетесь развернуть приложение на устройстве, но устройство, на которое вы хотите развернуть приложение, не отображается в диалоговом окне Select Device. Это может произойти, если отладочный мост Android решил уйти в отпуск.

Чтобы диагностировать эту проблему, найдите программу adb и запустите ее:

adb devices

Если устройство отсутствует, то необходимо перезапустить сервер Android Debug Bridge, чтобы устройство было найдено:

adb kill-server
adb start-server

Программное обеспечение HTC Sync может препятствовать корректной работе команды adb start-server. Если команда adb start-server не выводит информацию о том, на каком порту он запускается, выйдите из программы HTC Sync и попробуйте перезапустить сервер adb.

Не удалось запустить указанный исполняемый файл задачи keytool

Это означает, что в вашем PATH отсутствует каталог, в котором находится каталог bin Java SDK. Проверьте, что вы выполнили все шаги из руководства по установке.

Monodroid.exe или aresgen.exe завершился с кодом 1

Чтобы помочь отладить эту проблему, войдите в Visual Studio и измените уровень вербозности MSBuild, для этого выберите: Tools > Options > Project and Solutions > Build and Run > MSBuild Project Build Output Verbosity и установите значение Normal.

Пересоберите проект и проверьте панель Visual Studio Output, которая должна содержать полную информацию об ошибке.

На устройстве недостаточно места для развертывания пакета

Эта ошибка возникает, когда эмулятор запускается не из Visual Studio. При запуске эмулятора вне Visual Studio необходимо передать опцию -partition-size 512, например

emulator -partition-size 512 -avd MonoDroid

INSTALL_FAILED_INVALID_APK при установке пакета

Имена пакетов Android должны содержать точку ('.'). Отредактируйте имя пакета так, чтобы оно содержало точку.

В Visual Studio:

  • Щелкните правой кнопкой мыши проект > Свойства
  • Перейдите на вкладку Android Manifest слева.
  • Обновите поле "Имя пакета".
  • Если появится сообщение "AndroidManifest.xml не найден. Click to add one.", щелкните ссылку, а затем обновите поле Package name.

В Visual Studio для Mac:

  • Щелкните правой кнопкой мыши свой проект > Options.
  • Перейдите в раздел Build / Android Application.
  • Измените поле Package name так, чтобы оно содержало символ '.'.

INSTALL_FAILED_MISSING_SHARED_LIBRARY при установке пакета

В данном контексте «общая библиотека» - это не родной файл общей библиотеки (libfoo.so), а библиотека, которая должна быть отдельно установлена на целевом устройстве, например Google Maps.

Пакет Android указывает, какие разделяемые библиотеки требуются, с помощью элемента . Если требуемая библиотека отсутствует на целевом устройстве (например, //uses-library/@android:required is true, что является значением по умолчанию), то установка пакета завершится с ошибкой INSTALL_FAILED_MISSING_SHARED_LIBRARY.

Чтобы определить, какие разделяемые библиотеки требуются, просмотрите сгенерированный файл AndroidManifest.xml (например, obj\Debug\android\AndroidManifest.xml) и найдите в нем элементы . Элементы могут быть добавлены вручную в файл Properties\AndroidManifest.xml вашего проекта и с помощью пользовательского атрибута UsesLibraryAttribute.

Например, добавление ссылки на сборку Mono.Android.GoogleMaps.dll неявно добавит для разделяемой библиотеки Google Maps.

INSTALL_FAILED_UPDATE_INCOMPATIBLE при установке пакета

К пакетам Android предъявляются три требования:

  • Они должны содержать символ '.' (см. предыдущую запись).
  • Они должны иметь уникальное строковое имя пакета (поэтому в именах приложений Android используется соглашение reverse-tld, например, com.android.chrome для приложения Chrome).
  • При обновлении пакетов они должны иметь один и тот же ключ подписи.

Таким образом, представьте себе следующий сценарий:

  • Вы собираете и развертываете свое приложение в качестве приложения Debug.
  • Вы меняете ключ подписи, например, для использования в качестве приложения Release (или потому, что вам не нравится ключ подписи Debug, предоставляемый по умолчанию).
  • Вы устанавливаете приложение без его предварительного удаления, например, Debug > Start Without Debugging в Visual Studio.

В этом случае установка пакета завершится с ошибкой INSTALL_FAILED_UPDATE_INCOMPATIBLE, поскольку имя пакета не изменилось, а ключ подписи изменился. В журнале отладки Android также появится сообщение, аналогичное:

E/PackageManager( 146): Package [PackageName] signatures do not match the previously installed version; ignoring!

Чтобы устранить эту ошибку, полностью удалите приложение с устройства перед повторной установкой.

INSTALL_FAILED_UID_CHANGED при установке пакета

При установке пакета Android ему присваивается идентификатор пользователя (UID). Иногда, по неизвестным на данный момент причинам, при установке поверх уже установленного приложения происходит сбой установки с сообщением INSTALL_FAILED_UID_CHANGED:

ERROR [2015-03-23 11:19:01Z]: ANDROID: Deployment failed
Mono.AndroidTools.InstallFailedException: Failure [INSTALL_FAILED_UID_CHANGED]
  at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName)
  at Mono.AndroidTools.AndroidDevice.<>c__DisplayClass2c.b__2b(Task`1 t)
  at System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
  at System.Threading.Tasks.Task.Execute()

Чтобы решить эту проблему, полностью удалите пакет Android, либо установив приложение из графического интерфейса целевой программы Android, либо используя adb:

$ adb uninstall @PACKAGE_NAME@

НЕ ИСПОЛЬЗУЙТЕ adb uninstall -k, так как в этом случае данные приложения будут сохранены, а значит, сохранится и конфликтующий UID на целевом устройстве.

Релизные приложения не запускаются на устройстве

Если в журнале отладки Android появится сообщение, аналогичное:

D/AndroidRuntime( 1710): Shutting down VM
W/dalvikvm( 1710): threadid=1: thread exiting with uncaught exception (group=0xb412f180)
E/AndroidRuntime( 1710): FATAL EXCEPTION: main
E/AndroidRuntime( 1710): java.lang.UnsatisfiedLinkError: Couldn't load monodroid: findLibrary returned null
E/AndroidRuntime( 1710):       at java.lang.Runtime.loadLibrary(Runtime.java:365)

Если это так, то возможны две причины:

  • В .apk не указан ABI, поддерживаемый целевым устройством. Например, .apk содержит только двоичные файлы armeabi-v7a, а целевое устройство поддерживает только armeabi.
  • Это ошибка Android. В этом случае удалите приложение, скрестите пальцы и установите его заново.

Чтобы исправить (1), отредактируйте параметры проекта (Project Options/Properties) и добавьте поддержку требуемого ABI в список Supported ABIs. Чтобы определить, какой ABI необходимо добавить, выполните следующую команду adb на целевом устройстве:

adb shell getprop ro.product.cpu.abi
adb shell getprop ro.product.cpu.abi2

В результате будут получены первичные (и, по желанию, вторичные) ABI.

$ adb shell getprop | grep ro.product.cpu
[ro.product.cpu.abi2]: [armeabi]
[ro.product.cpu.abi]: [armeabi-v7a]

Свойство OutPath не установлено для проекта "MyApp.csproj"

Обычно это означает, что у вас компьютер HP и переменная окружения "Platform" имеет значение MCD или HPD. Это противоречит свойству MSBuild Platform, которое обычно устанавливается на "Any CPU" или "x86". Чтобы MSBuild мог работать, необходимо удалить эту переменную окружения с компьютера:

  • Панель управления > Система > Дополнительно > Переменные среды

Перезапустите Visual Studio или Visual Studio for Mac и попробуйте выполнить сборку. Теперь все должно работать так, как ожидалось.

java.lang.ClassCastException: mono.android.runtime.JavaObject cannot be cast to...

Xamarin.Android 4.x неправильно выстраивает вложенные общие типы. Например, рассмотрим следующий C#-код с использованием SimpleExpandableListAdapter:

// BAD CODE; DO NOT USE
var groupData = new List<_string2c_ object="">> () {
       new Dictionary<_string2c_ object=""> {
               { "NAME", "Group 1" },
               { "IS_EVEN", "This group is odd" },
       },
};
var childData = new List<_string2c_ object="">>> () {
       new List<_string2c_ object="">> {
               new Dictionary<_string2c_ object=""> {
                       { "NAME", "Child 1" },
                       { "IS_EVEN", "This group is odd" },
               },
       },
};
mAdapter = new SimpleExpandableListAdapter (
       this,
       groupData,
       Android.Resource.Layout.SimpleExpandableListItem1,
       new string[] { "NAME", "IS_EVEN" },
       new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 },
       childData,
       Android.Resource.Layout.SimpleExpandableListItem2,
       new string[] { "NAME", "IS_EVEN" },
       new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 }
);

Проблема заключается в том, что Xamarin.Android некорректно выстраивает вложенные общие типы. Список List<_string2c_ object="">> маршализируется в java.lang.ArrrayList, но ArrayList содержит экземпляры mono.android.runtime.JavaObject (которые ссылаются на экземпляры Dictionary<_string2c_ object="">), а не что-то, реализующее java.util.Map, что приводит к следующему исключению:

E/AndroidRuntime( 2991): FATAL EXCEPTION: main
E/AndroidRuntime( 2991): java.lang.ClassCastException: mono.android.runtime.JavaObject cannot be cast to java.util.Map
E/AndroidRuntime( 2991):       at android.widget.SimpleExpandableListAdapter.getGroupView(SimpleExpandableListAdapter.java:278)
E/AndroidRuntime( 2991):       at android.widget.ExpandableListConnector.getView(ExpandableListConnector.java:446)
E/AndroidRuntime( 2991):       at android.widget.AbsListView.obtainView(AbsListView.java:2271)
E/AndroidRuntime( 2991):       at android.widget.ListView.makeAndAddView(ListView.java:1769)
E/AndroidRuntime( 2991):       at android.widget.ListView.fillDown(ListView.java:672)
E/AndroidRuntime( 2991):       at android.widget.ListView.fillFromTop(ListView.java:733)
E/AndroidRuntime( 2991):       at android.widget.ListView.layoutChildren(ListView.java:1622)

Обходным решением является использование для «внутренних» типов предоставленных типов Java Collection вместо типов System.Collections.Generic. В результате при маршалинге экземпляров будут получены соответствующие Java-типы. (Приведенный ниже код является более сложным, чем это необходимо для сокращения времени жизни gref. Если время жизни gref не вызывает опасений, можно ограничиться изменением исходного кода с помощью s/List/JavaList/g и s/Dictionary/JavaDictionary/g).

// insert good code here
using (var groupData = new JavaList<_string2c_ object="">> ()) {
   using (var groupEntry = new JavaDictionary<_string2c_ object=""> ()) {
       groupEntry.Add ("NAME", "Group 1");
       groupEntry.Add ("IS_EVEN", "This group is odd");
       groupData.Add (groupEntry);
   }
   using (var childData = new JavaList<_string2c_ object="">>> ()) {
       using (var childEntry = new JavaList<_string2c_ object="">> ())
       using (var childEntryDict = new JavaDictionary<_string2c_ object=""> ()) {
           childEntryDict.Add ("NAME", "Child 1");
           childEntryDict.Add ("IS_EVEN", "This child is odd.");
           childEntry.Add (childEntryDict);
           childData.Add (childEntry);
       }
       mAdapter = new SimpleExpandableListAdapter (
           this,
           groupData,
           Android.Resource.Layout.SimpleExpandableListItem1,
           new string[] { "NAME", "IS_EVEN" },
           new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 },
           childData,
           Android.Resource.Layout.SimpleExpandableListItem2,
           new string[] { "NAME", "IS_EVEN" },
           new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 }
       );
   }
}

Неожиданные NullReferenceExceptions

Иногда в журнале отладки Android появляются упоминания о NullReferenceExceptions, которые «не могут произойти» или возникают из кода среды выполнения Mono for Android незадолго до завершения работы приложения:

E/mono(15202): Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object
E/mono(15202):  at Java.Lang.Object.GetObject (IntPtr handle, System.Type type, Boolean owned)
E/mono(15202):  at Java.Lang.Object._GetObject[IOnTouchListener] (IntPtr handle, Boolean owned)
E/mono(15202):  at Java.Lang.Object.GetObject[IOnTouchListener] (IntPtr handle, Boolean owned)
E/mono(15202):  at Android.Views.View+IOnTouchListenerAdapter.n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent_(IntPtr jnienv, IntPtr native__this, IntPtr native_v, IntPtr native_e)
E/mono(15202):  at (wrapper dynamic-method) object:b039cbb0-15e9-4f47-87ce-442060701362 (intptr,intptr,intptr,intptr)

Или

E/mono   ( 4176): Unhandled Exception:
E/mono   ( 4176): System.NullReferenceException: Object reference not set to an instance of an object
E/mono   ( 4176): at Android.Runtime.JNIEnv.NewString (string)
E/mono   ( 4176): at Android.Util.Log.Info (string,string)

Это может произойти, когда среда выполнения Android решит прервать процесс, что может произойти по разным причинам, включая превышение целевого лимита GREF или что-то "не то" с JNI.

Чтобы проверить, так ли это, проверьте журнал отладки Android на наличие сообщения от процесса, похожего на:

E/dalvikvm( 123): VM aborting

Прерывание по причине исчерпания глобальных ссылок

Уровень JNI среды исполнения Android поддерживает только ограниченное количество ссылок на объекты JNI, которые могут быть действительны в любой момент времени. При превышении этого лимита происходит сбой.

Ограничение GREF (global reference) составляет 2000 ссылок в эмуляторе и ~52000 ссылок на аппаратном обеспечении.

О том, что вы начинаете создавать слишком много GREF, можно судить по появлению в журнале отладки Android сообщений, подобных этому:

D/dalvikvm( 602): GREF has increased to 1801

При достижении предела GREF на печать выводится сообщение, подобное следующему:

D/dalvikvm( 602): GREF has increased to 2001
W/dalvikvm( 602): Last 10 entries in JNI global reference table:
W/dalvikvm( 602): 1991: 0x4057eff8 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): 1992: 0x4057f010 cls=Landroid/graphics/Point; (28 bytes)
W/dalvikvm( 602): 1993: 0x40698e70 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): 1994: 0x40698e88 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): 1995: 0x40698ea0 cls=Landroid/graphics/Point; (28 bytes)
W/dalvikvm( 602): 1996: 0x406981f0 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): 1997: 0x40698208 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): 1998: 0x40698220 cls=Landroid/graphics/Point; (28 bytes)
W/dalvikvm( 602): 1999: 0x406956a8 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): 2000: 0x406956c0 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm( 602): JNI global reference table summary (2001 entries):
W/dalvikvm( 602):   51 of Ljava/lang/Class; 164B (41 unique)
W/dalvikvm( 602):   46 of Ljava/lang/Class; 188B (17 unique)
W/dalvikvm( 602):    6 of Ljava/lang/Class; 212B (6 unique)
W/dalvikvm( 602):   11 of Ljava/lang/Class; 236B (7 unique)
W/dalvikvm( 602):    3 of Ljava/lang/Class; 260B (3 unique)
W/dalvikvm( 602):    4 of Ljava/lang/Class; 284B (2 unique)
W/dalvikvm( 602):    8 of Ljava/lang/Class; 308B (6 unique)
W/dalvikvm( 602):    1 of Ljava/lang/Class; 316B
W/dalvikvm( 602):    4 of Ljava/lang/Class; 332B (3 unique)
W/dalvikvm( 602):    1 of Ljava/lang/Class; 356B
W/dalvikvm( 602):    2 of Ljava/lang/Class; 380B (1 unique)
W/dalvikvm( 602):    1 of Ljava/lang/Class; 428B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 452B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 476B
W/dalvikvm( 602):    2 of Ljava/lang/Class; 500B (1 unique)
W/dalvikvm( 602):    1 of Ljava/lang/Class; 548B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 572B
W/dalvikvm( 602):    2 of Ljava/lang/Class; 596B (2 unique)
W/dalvikvm( 602):    1 of Ljava/lang/Class; 692B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 956B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 1004B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 1148B
W/dalvikvm( 602):    2 of Ljava/lang/Class; 1172B (1 unique)
W/dalvikvm( 602):    1 of Ljava/lang/Class; 1316B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 3428B
W/dalvikvm( 602):    1 of Ljava/lang/Class; 3452B
W/dalvikvm( 602):    1 of Ljava/lang/String; 28B
W/dalvikvm( 602):    2 of Ldalvik/system/VMRuntime; 12B (1 unique)
W/dalvikvm( 602):   10 of Ljava/lang/ref/WeakReference; 28B (10 unique)
W/dalvikvm( 602):    1 of Ldalvik/system/PathClassLoader; 44B
W/dalvikvm( 602): 1553 of Landroid/graphics/Point; 20B (1553 unique)
W/dalvikvm( 602):  261 of Landroid/graphics/Point; 28B (261 unique)
W/dalvikvm( 602):    1 of Landroid/view/MotionEvent; 100B
W/dalvikvm( 602):    1 of Landroid/app/ActivityThread$ApplicationThread; 28B
W/dalvikvm( 602):    1 of Landroid/content/ContentProvider$Transport; 28B
W/dalvikvm( 602):    1 of Landroid/view/Surface$CompatibleCanvas; 44B
W/dalvikvm( 602):    1 of Landroid/view/inputmethod/InputMethodManager$ControlledInputConnectionWrapper; 36B
W/dalvikvm( 602):    1 of Landroid/view/ViewRoot$1; 12B
W/dalvikvm( 602):    1 of Landroid/view/ViewRoot$W; 28B
W/dalvikvm( 602):    1 of Landroid/view/inputmethod/InputMethodManager$1; 28B
W/dalvikvm( 602):    1 of Landroid/view/accessibility/AccessibilityManager$1; 28B
W/dalvikvm( 602):    1 of Landroid/widget/LinearLayout$LayoutParams; 44B
W/dalvikvm( 602):    1 of Landroid/widget/LinearLayout; 332B
W/dalvikvm( 602):    2 of Lorg/apache/harmony/xnet/provider/jsse/TrustManagerImpl; 28B (1 unique)
W/dalvikvm( 602):    1 of Landroid/view/SurfaceView$MyWindow; 36B
W/dalvikvm( 602):    1 of Ltouchtest/RenderThread; 92B
W/dalvikvm( 602):    1 of Landroid/view/SurfaceView$3; 12B
W/dalvikvm( 602):    1 of Ltouchtest/DrawingView; 412B
W/dalvikvm( 602):    1 of Ltouchtest/Activity1; 180B
W/dalvikvm( 602): Memory held directly by tracked refs is 75624 bytes
E/dalvikvm( 602): Excessive JNI global references (2001)
E/dalvikvm( 602): VM aborting

В приведенном выше примере (который, кстати, относится к ошибке 685215) проблема заключается в том, что создается слишком много экземпляров Android.Graphics.Point; список исправлений этой ошибки приведен в комментарии №2.

Обычно полезным решением является поиск типа, для которого выделено слишком много экземпляров - Android.Graphics.Point в приведенном выше дампе - затем поиск мест их создания в исходном коде и соответствующее избавление от них (чтобы сократить время жизни Java-объекта). Это не всегда целесообразно (#685215 является многопоточным, поэтому тривиальное решение позволяет обойтись без вызова Dispose), но это первое, на что следует обратить внимание.

Чтобы узнать, когда создаются GREF-файлы и сколько их существует, можно включить ведение журнала GREF.

Прерывание из-за несоответствия типов JNI

При ручной обработке JNI-кода возможно некорректное совпадение типов, например, если вы попытаетесь вызвать java.lang.Runnable.run для типа, не реализующего java.lang.Runnable. В этом случае в журнале отладки Android появится сообщение, аналогичное этому:

W/dalvikvm( 123): JNI WARNING: can't call Ljava/Type;;.method on instance of Lanother/java/Type;
W/dalvikvm( 123):             in Lmono/java/lang/RunnableImplementor;.n_run:()V (CallVoidMethodA)
...
E/dalvikvm( 123): VM aborting

Поддержка динамического кода

Динамический код не компилируется

Для использования динамического кода C# в приложении или библиотеке необходимо добавить в проект System.Core.dll, Microsoft.CSharp.dll и Mono.CSharp.dll.

В сборке Release для динамического кода во время выполнения возникает исключение MissingMethodException

  • Вероятно, в проекте вашего приложения отсутствуют ссылки на System.Core.dll, Microsoft.CSharp.dll или Mono.CSharp.dll. Убедитесь, что на эти сборки есть ссылки.
  • - Помните, что динамический код всегда стоит дорого. Если вам нужен эффективный код, то лучше отказаться от использования динамического кода.
  • В первой предварительной версии эти сборки были исключены, если только типы в каждой сборке не используются явно в коде приложения. В качестве обходного пути см. следующее: http://lists.ximian.com/pipermail/mo...il/009798.html.

Проекты, созданные с использованием AOT+LLVM, аварийно завершаются на устройствах x86

При развертывании приложения, созданного с помощью AOT+LLVM, на устройствах на базе x86 может появиться сообщение об ошибке исключения, подобное следующему:

Assertion: should not be reached at /Users/.../external/mono/mono/mini/tramp-x86.c:124
Fatal signal 6 (SIGABRT), code -6 in tid 4051 (Xamarin.bug56111)
Оглавление

Материалы по теме