Перейти к содержанию

Инструментирование мобильных приложений с помощью Dynatrace .NET MAUI NuGet пакета

Dynatrace версии 1.265+

Dynatrace .NET MAUI NuGet пакет помогает автоматически инструментировать ваше мобильное приложение .NET MAUI с помощью OneAgent для Android и iOS, а также предоставляет API для ручного инструментирования.

Dynatrace .NET MAUI NuGet пакет доступен для Android и iOS. Вы не можете использовать наш пакет для macOS и Windows.

Поддерживаемые функции

Автоматическое инструментирование

  • Пользовательские действия
  • События жизненного цикла
  • Веб-запросы
  • Сбои

Ручное инструментирование

  • Пользовательские действия
  • Веб-запросы
  • Значения
  • События
  • Ошибки
  • Сбои
  • Тегирование пользователей

Требования

  • Для Android: Android версии 5.0+ (API 21+)
  • Для iOS: iOS версии 12+

Настройка пакета

Выполните следующие шаги для настройки Dynatrace .NET MAUI NuGet пакета для вашего мобильного приложения.

[Шаг 1

Установите Dynatrace .NET MAUI NuGet пакет](maui.md#install-package "Мониторинг приложений .NET MAUI с помощью Dynatrace OneAgent.")[Шаг 2

Создайте приложение и получите файл конфигурации](maui.md#installation-dynatrace "Мониторинг приложений .NET MAUI с помощью Dynatrace OneAgent.")[Шаг 3

Добавьте файл конфигурации в ваш проект](maui.md#configure-app "Мониторинг приложений .NET MAUI с помощью Dynatrace OneAgent.")[Шаг 4

Добавьте метод запуска OneAgent](maui.md#start-method "Мониторинг приложений .NET MAUI с помощью Dynatrace OneAgent.")[Шаг 5 (необязательно)

Включите автоматическое инструментирование веб-запросов](maui.md#http-client "Мониторинг приложений .NET MAUI с помощью Dynatrace OneAgent.")

Шаг 1. Установка NuGet пакета

Добавьте Dynatrace .NET MAUI NuGet пакет в ваше приложение.

  1. В Visual Studio щёлкните правой кнопкой мыши на решении вашего мобильного приложения и выберите Manage NuGet packages.
  2. Найдите Dynatrace.OneAgent.MAUI на nuget.org и выберите Add Package.
  3. Установите флажки для всех проектов, в которые вы хотите добавить NuGet пакет. Убедитесь, что вы добавляете пакет в нативные проекты, так как целевые файлы нашего пакета взаимодействуют со сборкой.
  4. Выберите OK.

Шаг 2. Создание приложения и получение файла конфигурации

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

  1. В Dynatrace перейдите в Mobile.
  2. Выберите Create mobile app.
  3. Введите имя вашего приложения и выберите Create mobile app. Откроется страница настроек приложения.
  4. В настройках приложения выберите Instrumentation wizard > .NET MAUI.
  5. На шаге 2 выберите Download dynatrace.config.json, чтобы получить файл конфигурации.

Шаг 3. Добавление файла конфигурации в проект

Добавьте файл dynatrace.config.json, который вы скачали на предыдущем шаге, в ваш проект.

Android

iOS

Добавьте файл dynatrace.config.json в директорию Platforms/Android/Assets вашего проекта.

Если вы не можете найти директорию Assets, создайте её внутри директории Platforms/Android.

Добавьте файл dynatrace.config.json в директорию Platforms/iOS/Resources вашего проекта.

Перед каждой сборкой наш пакет автоматически создаёт новый файл Dynatrace.plist на основе параметров, заданных в файле конфигурации.

Если вы не можете найти директорию Resources, создайте её внутри директории Platforms/iOS.

Шаг 4. Добавление метода запуска OneAgent

Метод запуска необходим для старта OneAgent.

Android

iOS

using Dynatrace.MAUI;


Agent.Instance.Start();
using Dynatrace.MAUI;


Agent.Instance.Start();

Шаг 5 (необязательно). Включение автоматического инструментирования веб-запросов (необязательно)

Вы можете использовать следующий метод для включения автоматического инструментирования веб-запросов. HttpMessageHandler, используемый HttpClient, берёт на себя ручное инструментирование веб-запросов.

using Dynatrace.MAUI;


var httpHandler = Agent.Instance.GetHttpMessageHandler();


var httpClient = new HttpClient(httpHandler);

Кроме того, вы можете использовать собственный HTTP-обработчик:

using Dynatrace.MAUI;


var defaultHttpHandler = new HttpClientHandler();


var httpHandler = Agent.Instance.GetHttpMessageHandler(defaultHttpHandler);


var httpClient = new HttpClient(httpHandler);

Ручное инструментирование

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

Запуск OneAgent

Вы можете использовать ручной запуск с помощью конфигурационного построителя (Android) или словаря конфигурации (iOS).

  1. Измените файл dynatrace.config.json, чтобы отключить автоматический запуск OneAgent.

Android

iOS

{


"android": {


"autoStart": {


"enabled": false


}


}


}
{


"ios": {


"DTXAutoStart": false


}


}

Не добавляйте дополнительные свойства в файл конфигурации. В противном случае сборка завершится с ошибкой. 2. Запустите OneAgent вручную и передайте необходимые свойства.

Android

iOS

using Dynatrace.MAUI;


Agent.Instance.Start(new ConfigurationBuilder("<insertBeaconURL>","<insertApplicationID>").BuildConfiguration());
using Dynatrace.MAUI;


Agent.Instance.Start(new ConfigurationBuilder("<insertBeaconURL>","<insertApplicationID>").BuildConfiguration());

Создание пользовательских действий

Вы можете создавать пользовательские действия и дополнять их информацией, такой как значения, события и ошибки.

Вызовите EnterAction, чтобы начать пользовательское действие, и LeaveAction, чтобы его завершить. Время измеряется автоматически.

using Dynatrace.MAUI;


IRootAction myAction = Agent.Instance.EnterAction("Tap on Confirm");


//Perform the action and whatever else is needed.


myAction.LeaveAction();

Максимальная длина имени мобильного пользовательского действия или автоматически сгенерированного пользовательского действия составляет 250 символов.

Информацию об именовании пользовательских действий смотрите по следующим ссылкам: Android и iOS.

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

Создание дочерних действий

Помимо создания самостоятельных пользовательских действий, вы также можете создавать дочерние действия.

Дочерние действия аналогичны родительским пользовательским действиям. При закрытии родительского действия все его дочерние действия автоматически закрываются.

using Dynatrace.MAUI;


IRootAction myAction = Agent.Instance.EnterAction("Tap on Confirm");


IAction mySubAction = myAction.EnterAction("Tap on Confirm again");


//Perform the action and whatever else is needed.


mySubAction.LeaveAction();


myAction.LeaveAction();

Максимальная длина имени мобильного пользовательского действия или автоматически сгенерированного пользовательского действия составляет 250 символов.

Количество дочерних действий, прикреплённых к пользовательскому действию, не ограничено. Однако обратите внимание, что допускается только один уровень вложенности дочерних действий — вы не можете создать дочернее действие для другого дочернего действия (дочерние действия не могут иметь собственных дочерних действий). Также см. Структура пользовательской сессии для отдельного пользователя.

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

Отмена пользовательских действий

Если вам нужно отменить уже созданное, но ещё не закрытое пользовательское действие, вызовите Cancel. Отмена действия удаляет все связанные с ним данные: все отправленные значения, события и ошибки отбрасываются; все дочерние действия отменяются.

using Dynatrace.MAUI;


IRootAction myAction = Agent.Instance.EnterAction("Tap on Confirm");


// Action is canceled


myAction.Cancel();

Вы не можете отменить закрытое действие, поэтому вызов Cancel после LeaveAction невозможен для одного и того же действия. То же самое относится к закрытию отменённого действия: вы не можете вызвать LeaveAction после использования Cancel для того же действия.

Инструментирование веб-запросов

Используйте следующий фрагмент кода для инструментирования веб-запросов:

using Dynatrace.MAUI;


// Create an action


IRootAction webAction = Agent.Instance.EnterAction(actionName: "WebRequest Action");


// Generate a new unique tag associated with the web request action


string requestTag = webAction.GetRequestTag(url);


string requestTagHeader = webAction.GetRequestTagHeader();


// Place the Dynatrace HTTP header on your web request


httpClient.DefaultRequestHeaders.Add(requestTagHeader, requestTag);


// Generate a WebRequestTiming object based on the unique tag


IWebRequestTiming timing = Agent.Instance.GetWebRequestTiming(requestTag, url);


// Start web request timing before the HTTP request is sent


timing.StartWebRequestTiming();


try


{


var response = await httpClient.GetAsync(url);


// Stop web request timing when the HTTP response is received and the response body is obtained


timing.StopWebRequestTiming(url, (int)response.StatusCode, response.ReasonPhrase);


}


catch (HttpRequestException exception)


{


// Stop web request timing when a connection exception occurs


timing.StopWebRequestTiming(url, -1, exception.ToString());


}


finally


{


// Leave an action


webAction.LeaveAction();


}

Отправка значения

Метод reportValue позволяет отправлять пары «ключ-значение» метаданных, которые вы можете затем просматривать в веб-интерфейсе Dynatrace и преобразовывать в свойства пользовательских действий и сессий. Отправляемые значения должны быть частью пользовательского действия.

Вы можете отправлять значения следующих типов данных:

  • int
  • double
  • string
ReportValue(valueName: string, value: int);


ReportValue(valueName: string, value: double);


ReportValue(valueName: string, value: string);

Например, чтобы отправить значение типа string в рамках действия Tap on Confirm, используйте следующий код:

using Dynatrace.MAUI;


IRootAction myAction = Agent.Instance.EnterAction("Tap on Confirm");


myAction.ReportValue("Customer type", "Gold");


myAction.LeaveAction();

Чтобы просмотреть отправленные значения в веб-интерфейсе Dynatrace, перейдите к деталям пользовательского действия, которое должно содержать эти метаданные, и прокрутите вниз до раздела Reported values.

Страница деталей пользовательского действия с отправленными через SDK значениями

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

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

Отправка события

Для любого открытого действия вы можете отправить событие. Используйте следующий вызов API:

ReportEvent(eventName: string);

Если вы хотите отправлять самостоятельные события с большим количеством дополнительной информации, см. Отправка бизнес-события.

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

Отправка ошибки

Чтобы отправить ошибку, используйте метод ReportError.

ReportError(errorName: string, errorCode: number);

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

Отправка трассировки стека ошибки

Чтобы отправить трассировку стека ошибки, используйте следующий вызов API:

using Dynatrace.MAUI;


Agent.Instance.ReportErrorStacktrace("Error_Class", "Error_Value", "Error_Reason", "Error_Stacktrace");

Отправка информации о сбое

Чтобы отправить информацию о сбое, используйте следующий вызов API.

using Dynatrace.MAUI;


Agent.Instance.ReportCrash("CrashWithoutException", "Crash_Reason", "Crash_Stacktrace");

Вы также можете использовать объект исключения:

using Dynatrace.MAUI;


Agent.Instance.ReportCrashWithException("CrashWithExceptionObj", exception);

Время отправки деталей сбоя в Dynatrace зависит от операционной системы вашего мобильного приложения.

  • Android

Как правило, детали сбоя отправляются сразу после сбоя, поэтому пользователю не нужно перезапускать приложение. Однако в некоторых случаях приложение необходимо повторно открыть в течение 10 минут, чтобы отчёт о сбое был отправлен. Обратите внимание, что Dynatrace не отправляет отчёты о сбоях старше 10 минут (такие отчёты уже не могут быть скоррелированы на кластере Dynatrace). * iOS

Детали сбоя отправляются только при повторном открытии мобильного приложения (то есть при следующем запуске). Однако, если пользователь не откроет приложение в течение 10 минут, отчёт о сбое удаляется. Это связано с тем, что Dynatrace не отправляет отчёты о сбоях старше 10 минут (такие отчёты уже не могут быть скоррелированы на кластере Dynatrace).

Отправка информации о сбое принудительно завершает пользовательскую сессию. Все последующие действия включаются в новую пользовательскую сессию.

Только для Android. При использовании автоматической отправки отчётов о сбоях Visual Studio может перехватить исключение раньше OneAgent. Если вы заметили, что Dynatrace не отправляет отчёты о сбоях в вашу среду, убедитесь, что вы не используете режим отладки в Visual Studio. В противном случае отладчик перехватывает сбой и ничего не отправляется в вашу среду Dynatrace.

Отправка бизнес-события

С помощью sendBizEvent вы можете отправлять бизнес-события. Это самостоятельные события, поскольку Dynatrace отправляет их отдельно от пользовательских действий или сессий.

Бизнес-события фиксируются только для мониторимых сессий. Когда OneAgent отключён с помощью специального флага или из-за контроля стоимости и трафика, бизнес-события для таких сессий не отправляются. Обратите внимание, что это поведение может измениться в будущем, и бизнес-события смогут отправляться в Dynatrace независимо от мониторинга сессий.

Для получения дополнительной информации о бизнес-событиях см. Бизнес-наблюдаемость.

using Dynatrace.MAUI;


var attributes = new Dictionary<string, JsonValue>();


attributes.Add("event.name", "Confirmed Booking");


attributes.Add("screen", "booking-confirmation");


attributes.Add("product", "Danube Anna Hotel");


attributes.Add("amount", 358.35);


attributes.Add("currency", "USD");


attributes.Add("reviewScore", 4.8);


attributes.Add("arrivalDate", "2022-11-05");


attributes.Add("departureDate", "2022-11-15");


attributes.Add("journeyDuration", 10);


attributes.Add("adultTravelers", 2);


attributes.Add("childrenTravelers", 0);


Agent.Instance.SendBizEvent("com.easytravel.funnel.booking-finished", attributes);

Тегирование конкретных пользователей

Вы можете тегировать каждого пользователя вашего мобильного приложения уникальным именем. Это позволяет искать и фильтровать конкретные пользовательские сессии и анализировать поведение отдельных пользователей с течением времени. Подробнее см. Тегирование пользователей.

Выполните следующий вызов API, чтобы присвоить тег текущей сессии:

using Dynatrace.MAUI;


Agent.Instance.IdentifyUser("John Smith");

OneAgent для Android версии 237+ OneAgent для iOS версии 235+ Сессии, разделённые из-за таймаута бездействия или длительности, автоматически получают повторный тег.

Когда OneAgent завершает помеченную сессию из-за достижения максимальной продолжительности или бездействия пользователя, следующая сессия автоматически получает повторный тег. Вам не нужно повторно предоставлять информацию для идентификации пользователя.

Однако обратите внимание, что OneAgent не присваивает повторный тег следующей сессии в следующих случаях:

  • Когда вы явно завершаете помеченную пользовательскую сессию через endVisit
  • Когда пользователь или мобильная операционная система закрывает или принудительно останавливает приложение
  • Когда OneAgent завершает текущую пользовательскую сессию и создаёт новую после изменения настроек конфиденциальности

См. Пользовательские сессии > Завершение сессии, чтобы узнать, когда OneAgent завершает мобильную пользовательскую сессию.

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

Завершение сессии

Вы можете принудительно завершить сессию через вызов API. Это также закрывает все открытые действия и начинает новую сессию.

using Dynatrace.MAUI;


Agent.Instance.EndVisit();

Настройка конфиденциальности данных (режим согласия)

В режиме согласия пользователя каждый пользователь вашего приложения может устанавливать свои предпочтения конфиденциальности и решать, хочет ли он делиться своей информацией. Когда режим согласия включён, вам необходимо запрашивать у каждого пользователя разрешение на сбор его данных, а затем сохранять его предпочтения конфиденциальности. Подробнее см. Режим согласия пользователя.

Включение режима согласия пользователя

Чтобы активировать режим согласия пользователя, установите свойство userOptIn (Android) или ключ конфигурации DTXUserOptIn (iOS) в значение true в файле dynatrace.config.json.

Получение предпочтений конфиденциальности пользователя

Вы можете получить предпочтения конфиденциальности конкретного пользователя.

Чтобы получить текущую конфигурацию UserPrivacyOptions, используйте следующий вызов API:

using Dynatrace.MAUI;


// Get the UserPrivacyOptions object


UserPrivacyOptions currentOptions = Agent.Instance.GetUserPrivacyOptions();


// Get the individual settings for DataCollectionLevel and crash reporting


bool crashOptedIn = Agent.Instance.GetUserPrivacyOptions().CrashReportingOptedIn;


DataCollectionLevel dataCollectionLevel = Agent.Instance.GetUserPrivacyOptions().DataCollectionLevel;

Изменение предпочтений конфиденциальности пользователя

Вы можете изменить предпочтения конфиденциальности на основе решения конкретного пользователя.

Чтобы задать новые параметры объекта UserPrivacyOptions, используйте следующий код:

using Dynatrace.MAUI;


// Creating a new UserPrivacyOptions object requires setting the two parameters of DataCollectionLevel and crash reporting


UserPrivacyOptions options = new UserPrivacyOptions(DataCollectionLevel.Performance, false);


// Update the options with the setter


// Set a data collection level (user allowed you to capture performance and personal data)


options.DataCollectionLevel = DataCollectionLevel.UserBehavior;


// Allow crash reporting (user allowed you to collect information on crashes)


options.CrashReportingOptedIn = true;


// Get the values of the configuration with the getter


options.DataCollectionLevel;


options.CrashReportingOptedIn;


// Get the UserPrivacyOptions object


UserPrivacyOptions currentOptions = Agent.Instance.GetUserPrivacyOptions();

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

using Dynatrace.MAUI;


UserPrivacyOptions options = new UserPrivacyOptions(DataCollectionLevel.UserBehavior, true);


Agent.Instance.ApplyUserPrivacyOptions(options);

Возможные значения для уровня сбора данных:

  • Off
  • Performance
  • UserBehavior

Отправка GPS-координат

Вы можете отправлять широту и долготу.

SetGPSLocation(latitude: double, longitude: double);

Накладные расходы инструментирования

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

Операционная система Шаблон приложения Версия Размер до Размер после Разница
Android Приложение по умолчанию net8.0-android 31,8 МБ 32,4 МБ 0,6 МБ
iOS Приложение по умолчанию net8.0-ios 421 МБ 426,2 МБ 5,2 МБ

Файл конфигурации

Файл конфигурации dynatrace.config.json содержит идентификатор вашего приложения, URL маяка и некоторые другие настройки.

  • Вы можете скачать этот файл из Dynatrace или создать его вручную.
  • Если вы не добавите файл конфигурации хотя бы со свойствами URL маяка и идентификатора приложения, сборка завершится с ошибкой. В качестве альтернативы используйте ручной запуск с конфигурационным построителем (Android) или словарём конфигурации (iOS).
  • Когда вы используете конкретную конфигурацию сборки — например, Debug, Release или пользовательскую конфигурацию — наш пакет ищет в директории Assets (Android) или Resources (iOS) файл конфигурации с именем dynatrace<Configuration>.config.json. Например, при использовании конфигурации сборки Debug наш пакет ищет файл с именем dynatraceDebug.config.json.
  • Если вы хотите указать пользовательский путь для конфигурации, задайте его через свойство DynatraceConfigurationFile.

Создайте Directory.Build.props в директории проекта Android/iOS (или общей):

<Project>


<PropertyGroup>


<DynatraceConfigurationFile>CUSTOM_PATH/dynatrace.config.json</DynatraceConfigurationFile>


</PropertyGroup>


</Project>

В итоге конфигурация используется в следующем порядке:

  1. Пользовательский путь конфигурации через свойство DynatraceConfigurationFile
  2. Файл, специфичный для конфигурации, например dynatrace<Configuration>.config.json
  3. Имя по умолчанию dynatrace.config.json

Ниже приведена структура файла dynatrace.config.json для Android и iOS.

Android

iOS

{


"android": {


"autoStart": {


"applicationId": "<insertApplicationID>",


"beaconUrl": "<insertBeaconURL>"


},


"userOptIn": true,


"agentBehavior": {


"startupLoadBalancing": true


}


}


}
{


"ios": {


"DTXApplicationId": "<insertApplicationID>",


"DTXBeaconUrl": "<insertBeaconURL>",


"DTXUserOptIn": true,


"DTXStartupLoadBalancing": true


}


}

Никогда не используйте точечную нотацию для файла конфигурации. Всегда используйте полный стиль записи с фигурными скобками.

Включение отладочных журналов OneAgent

Если инструментирование проходит успешно и ваше приложение запускается, но вы не видите данных в среде Dynatrace, вам, вероятно, нужно углубиться в анализ, чтобы выяснить, почему OneAgent не отправляет данные. Создание обращения в техподдержку — хорошая идея, но сначала лучше собрать журналы.

Android

iOS

Обновите ваш файл dynatrace.config.json, чтобы включить отладочные журналы OneAgent.

{


"android": {


"autoStart": {


"applicationId": "<insertApplicationID>",


"beaconUrl": "<insertBeaconURL>"


},


"userOptIn": true,


"debug": {


"agentLogging": true


}


}


}

Добавьте следующий фрагмент конфигурации в файл dynatrace.config.json:

{


"ios": {


"DTXApplicationId": "<insertApplicationID>",


"DTXBeaconUrl": "<insertBeaconURL>",


"DTXUserOptIn": true,


"DTXLogLevel": "ALL"


}


}

Включение отладочных журналов сборки для Android

Только для Android

Если инструментирование Android завершается с ошибкой, вам, скорее всего, нужно создать обращение в техподдержку и предоставить отладочные журналы сборки. Для получения этих журналов вам необходимо установить свойство DynatraceInstrumentationLogging и изменить уровень журнала сборки на Diagnostic.

  1. Установите свойство DynatraceInstrumentationLogging. Выберите один из следующих способов:

  2. Создайте Directory.Build.props в директории проекта Android:

<Project>


<PropertyGroup>


<DynatraceInstrumentationLogging>true</DynatraceInstrumentationLogging>


</PropertyGroup>


</Project>
  • Добавьте свойство DynatraceInstrumentationLogging в файл .csproj вашего проекта. Вставьте его в существующую PropertyGroup в зависимости от конфигурации, которую вы используете.
  • Измените уровень детализации вывода сборки на Diagnostic. Подробнее см. документацию Microsoft о том, как изменить объём информации в журнале сборки.
  • Пересоберите проект.
  • Приложите журналы сборки к обращению в техподдержку, чтобы мы могли проанализировать вашу проблему.

Устранение неполадок

Если вы не можете решить проблему, ознакомьтесь с разделом Мобильные приложения: проблемы с Dynatrace .NET MAUI NuGet пакетом в сообществе Dynatrace.

Связанные темы

  • Инструментирование Android-приложений
  • Инструментирование iOS-приложений