Часть 4. Работа с несколькими платформами

Обработка дивергенции платформы и функций

Расхождение не просто является проблемой кроссплатформенного; устройства на "той же" платформе имеют различные возможности (особенно широкий спектр доступных устройств Android). Наиболее очевидным и основным является размер экрана, но другие атрибуты устройства могут отличаться и требовать, чтобы приложение проверка для определенных возможностей и вести себя по-разному в зависимости от их присутствия (или отсутствия).

Это означает, что все приложения должны иметь дело с корректной деградацией функциональных возможностей, а также представлять непривлекательный, самый низкий общий набор функций. Глубокая интеграция Xamarin с собственными пакетами SDK для каждой платформы позволяет приложениям воспользоваться преимуществами функциональных возможностей конкретной платформы, поэтому рекомендуется разрабатывать приложения для использования этих функций.

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

Примеры расхождения платформ

Основные элементы, которые существуют на разных платформах

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

  • Выбор компонентов с помощью вкладок или меню
  • Списки данных и прокрутки
  • Одно представление данных
  • Редактирование отдельных представлений данных
  • Навигация назад

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

Атрибуты для конкретной платформы

Помимо основных элементов, которые существуют на всех платформах, необходимо решить основные различия платформы в проектировании. Вам может потребоваться рассмотреть (и написать код специально для обработки) эти различия:

  • Размеры экрана — некоторые платформы (например, iOS и более ранние версии Windows Телефон) имеют стандартные размеры экрана, которые относительно просты для целевого объекта. Устройства Android имеют большое количество измерений экрана, которые требуют больше усилий для поддержки в приложении.
  • Метафоры навигации — различаются между платформами (например, аппаратной кнопкой "назад", элементом управления пользовательским интерфейсом Panorama) и в платформах (Android 2 и 4, i Телефон vs iPad).
  • Клавиатуры — некоторые устройства Android имеют физические клавиатуры , а другие имеют только программную клавиатуру. Код, определяющий, когда обратимая клавиатура не учитывает часть экрана, должна быть чувствительной к этим различиям.
  • Сенсорные и жесты — поддержка распознавания жестов зависит, особенно в более ранних версиях каждой операционной системы. Более ранние версии Android имеют очень ограниченную поддержку сенсорных операций, что означает, что для поддержки старых устройств может потребоваться отдельный код
  • Push-уведомления — существуют различные возможности и реализации на каждой платформе (например. Динамические плитки в Windows).

Функции, относящиеся к устройству

Определение минимальных функций, необходимых для приложения; или при выборе дополнительных функций, которые следует использовать на каждой платформе. Код потребуется для обнаружения функций и отключения функциональных возможностей или альтернативных вариантов предложения (например, альтернативой географическому расположению может быть разрешение пользователю ввести расположение или выбрать из карты):

  • Камера — функциональные возможности различаются на разных устройствах: некоторые устройства не имеют камеры, другие имеют как передние, так и задние камеры. Некоторые камеры способны записывать видео.
  • Геолокация и карты — поддержка GPS или Wi-Fi не присутствует на всех устройствах. Приложения также должны поддерживать различные уровни точности, поддерживаемые каждым методом.
  • Accelerometer, gyro область и компас . Эти функции часто находятся только в выборе устройств на каждой платформе, поэтому приложения почти всегда должны обеспечить резервный вариант, когда оборудование не поддерживается.
  • Twitter и Facebook — только встроенные в iOS5 и iOS6 соответственно. На более ранних версиях и других платформах необходимо предоставить собственные функции проверки подлинности и интерфейс непосредственно с API каждой службы.
  • Near Field Communications (NFC) — только на (некоторых) телефонах Android (во время написания).

Работа с расхождением платформ

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

  • Абстракция платформы — шаблон бизнес-фасада обеспечивает единый доступ на разных платформах и абстрагирует конкретные реализации платформ в единый унифицированный API.
  • Дивергентная реализация — вызов конкретных функций платформы с помощью разных реализаций через архитектурные инструменты, такие как интерфейсы и наследование или условная компиляция.

Абстракция платформы

Абстракция классов

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

Интерфейсы

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

Интерфейс определяется в общем коде и передается в общую библиотеку как параметр или свойство.

Приложения, относящиеся к платформе, могут реализовать интерфейс и по-прежнему использовать общий код для его обработки.

Преимущества

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

Недостатки

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

Наследование

Общий код может реализовать абстрактные или виртуальные классы, которые могут быть расширены в одном или нескольких проектах для конкретной платформы. Это похоже на использование интерфейсов, но с некоторым поведением уже реализовано. Существуют различные точки зрения, независимо от того, являются ли интерфейсы или наследование лучшим вариантом проектирования: в частности, поскольку C# разрешает только одно наследование, оно может диктовать способ разработки API-интерфейсов. Используйте наследование с осторожностью.

Преимущества и недостатки интерфейсов применяются в равной степени к наследованию с дополнительными преимуществами, которые базовый класс может содержать некоторый код реализации (возможно, всю реализацию платформы, которая может быть дополнительно расширена).

Xamarin.Forms

См. документацию по Xamarin.Forms.

Другие кроссплатформенные библиотеки

Эти библиотеки также предлагают кроссплатформенную функциональность для разработчиков C#:

  • Xamarin.Essentials — кроссплатформенные API для общих функций.
  • SkiaSharp — кроссплатформенная 2D графика.

Условная компиляция

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

Проекты Xamarin всегда определяют __MOBILE__ , что верно для проектов приложений iOS и Android (обратите внимание на предварительное и после исправления двойного подчеркивания для этих символов).

#if __MOBILE__
// Xamarin iOS or Android-specific code
#endif

iOS

Xamarin.iOS определяет __IOS__ , что можно использовать для обнаружения устройств iOS.

#if __IOS__
// iOS-specific code
#endif

Существуют также символы для просмотра и телевизора:

#if __TVOS__
// tv-specific stuff
#endif

#if __WATCHOS__
// watch-specific stuff
#endif

Android

Код, который должен быть скомпилирован только в приложениях Xamarin.Android, может использовать следующее.

#if __ANDROID__
// Android-specific code
#endif

Каждая версия API также определяет новую директиву компилятора, поэтому такой код позволит добавлять функции, если новые API предназначены. Каждый уровень API включает все символы нижнего уровня. Эта функция не очень полезна для поддержки нескольких платформ; Обычно __ANDROID__ символ будет достаточно.

#if __ANDROID_11__
// code that should only run on Android 3.0 Honeycomb or newer
#endif

Mac

Xamarin.Mac определяет __MACOS__ , какие можно использовать для компиляции только для macOS:

#if __MACOS__
// macOS-specific code
#endif

Универсальная платформа Windows (UWP)

Используйте WINDOWS_UWP. Символы платформы Xamarin отсутствуют.

#if WINDOWS_UWP
// UWP-specific code
#endif

Использование условной компиляции

Простой пример условной компиляции — задание расположения файла для файла базы данных SQLite. Три платформы имеют немного разные требования к указанию расположения файла:

  • iOS — Apple предпочитает размещать данные, не являющиеся пользователями, в определенном расположении (каталог библиотеки), но для этого каталога нет системной константы. Для создания правильного пути требуется код для конкретной платформы.
  • Android — системный путь, возвращаемый Environment.SpecialFolder.Personal допустимым расположением для хранения файла базы данных.
  • Windows Телефон — изолированный механизм хранения не позволяет указывать полный путь, только относительный путь и имя файла.
  • универсальная платформа Windows — использует Windows.Storage API.

Следующий код использует условную компиляцию для обеспечения DatabaseFilePath правильности каждой платформы:

public static string DatabaseFilePath
{
    get
    {
        var filename = "TodoDatabase.db3";
#if SILVERLIGHT
        // Windows Phone 8
        var path = filename;
#else

#if __ANDROID__
        string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
#else
#if __IOS__
        // we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms
        // (they don't want non-user-generated data in Documents)
        string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
        string libraryPath = Path.Combine (documentsPath, "..", "Library");
#else
        // UWP
        string libraryPath = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
#endif
#endif
        var path = Path.Combine(libraryPath, filename);
#endif
        return path;
    }
}

Результатом является класс, который можно создать и использовать на всех платформах, разместив файл базы данных SQLite в другом расположении на каждой платформе.