Статьи

Доступ к собственным функциям с помощью Xamarin.Forms

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

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

Эти типы вопросов неизбежно вызывают некоторые проблемы для разработчиков, которые принимают новые технологии, такие как Xamarin.Forms. До выпуска Xamarin.Forms, когда вы работали непосредственно с шаблонами проектов Xamarin.iOS, Xamarin.Android и Windows Phone, доступ к этим типам функций был довольно простым. С точки зрения Xamarin, если бы вы могли найти образец C # — или даже документацию на родном языке и SDK — для конкретной функции, вы могли бы просто сопоставить свой код с нативными концепциями, потому что Xamarin проделал такую ​​потрясающую работу по переводу тех же нативных концепций на эти платформы в конструкции языка C #. Функции Windows Phone были еще проще, потому что перевод не требовался. Все, что вам нужно было сделать, это прочитать документацию.

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

На первый взгляд, такое имя, как DependencyService может показаться немного пугающим. Это звучит как некоторая причудливая терминология программирования, которую понимают лишь немногие элиты. Если вы когда-либо работали с контейнерами Dependency Injection (DI) или Inversion of Controller (IoC), вы должны чувствовать себя как дома с DependencyService . Если нет, уверяю вас, это очень простая концепция для понимания, если разбить ее на составляющие.

По своей сути DependencyService является классом. Это класс, единственная цель существования которого — позволить вам зарегистрировать любое количество классов в вашем приложении. Под регистрацией я имею в виду взять любой класс, который у вас есть, и сообщить об этом службе. Как только DependencyService узнает о классе, он может пойти и извлечь экземпляр этого класса при необходимости. Это еще одна цель DependencyService . Если в какой-то момент в вашем приложении вы решите, что вам нужен экземпляр класса, который был зарегистрирован в DependencyService , вы можете запросить или получить его экземпляр.

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

Теперь, когда у вас есть базовое понимание на концептуальном уровне, что такое DependencyService , давайте немного углубимся и посмотрим, как оно на самом деле работает.

Чтобы использовать DependencyService , вам нужны три вещи:

  1. Интерфейсы: Интерфейс — это просто конструкция, которая определяет, какие члены должны присутствовать в любом классе, который решит реализовать или согласиться с этим контрактом.
  2. Регистрация: Регистрация — это всего лишь механизм, позволяющий DependencyService знать, что определенный класс желает быть зарегистрированным и может быть получен позднее.
  3. Расположение: Эта концепция часто ассоциируется с шаблоном в разработке программного обеспечения, известным как шаблон Service Locator . Это просто означает, что вы можете перейти в одно место, DependencyService , и запросить некоторую функциональность, класс, без необходимости непосредственного создания нового экземпляра.

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

В наши дни интерфейсы очень распространены в большинстве языков объектно-ориентированного программирования (ООП). Использование интерфейса позволяет вам определять контракт, который содержит ряд свойств, методов, событий и т. Д., Которые должны быть реализованы любым классом, который соглашается с этим контрактом.

Вот очень простой пример интерфейса и класса, который реализует этот интерфейс.

01
02
03
04
05
06
07
08
09
10
11
12
public interface IFileGrabber
{
     string GetFileContents(string fileUri);
}
 
public SimpleGrabber : IFileGrabber
{
     public string GetFileContents(string fileUri)
     {
          return GetFileFromFileSystem(fileUri);
     }
}

Это выглядит как очень простой пример, но он служит цели довольно хорошо. Интерфейс IFileGrabber определяет единственный метод, GetFileContents . Класс SimpleGrabber соглашается или реализует интерфейс IFileGrabber , что означает, что он должен содержать реализацию для одного метода.

Теперь вместо того, чтобы реализовывать другой код в вашем приложении непосредственно против конкретного класса SimpleGrabber , вы можете вместо этого ссылаться на интерфейс IFileGrabber . Представьте, что у вас есть другой класс в вашем приложении, который выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class DataRetriever
{
    private IFileGrabber _fileGrabber;
     
    public DataRetriever(IFileGrabber fileGrabber)
    {
        _fileGrabber = fileGrabber
    }
 
     public string GetFileContents(string fileUri)
     {
          return _fileGrabber.GetFileContents(fileUri);
     }
}

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

1
2
3
4
5
6
7
public class NetworkGrabber : IFileGrabber
{
     public string GetFileContents(string fileUri)
     {
          return GetFileFromNetwork(fileUri);
     }
}

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

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

Давайте продолжим использовать приведенный выше пример и зарегистрируем класс SimpleGrabber . Определение класса теперь выглядело бы примерно так:

01
02
03
04
05
06
07
08
09
10
11
[assembly: Xamarin.Forms.Dependency(typeof(SimpleFileGrabber))]
 
// Any namespace declaration that may exist
 
public SimpleGrabber : IFileGrabber
{
     public string GetFileContents(string fileUri)
     {
          return GetFileFromFileSystem(fileUri);
     }
}

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

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

Последняя часть головоломки — получение зарегистрированного класса. Это на самом деле самая легкая часть всего процесса. Чтобы извлечь экземпляр зарегистрированного класса, вы просто используете класс DependencyService и это общий метод Get<>() . Вот простой пример:

1
2
3
4
5
6
7
public class FileHelper
{
    public string GetFileContents(string fileUri)
    {
        return DependencyService.Get<IFileGrabber>().GetFileContents(fileUri);
    }
}

В этом случае во время выполнения вам не важно, где DependencyService получает конкретный класс, который реализует интерфейс IFileGrabber . Все, что вас волнует, это то, что класс реализует интерфейс IFileGrabber .

Теперь, когда у вас есть концептуальное понимание того, что такое DependencyService и как его использовать, давайте создадим простое приложение для его использования.

В этом примере я буду использовать Xamarin Studio 5, но при желании вы можете использовать Visual Studio 2013. Начните с создания нового решения. В диалоговом окне « Новое решение » в категории « C # » слева выберите семейство проектов мобильных приложений . Справа выберите шаблон проекта Пустое приложение (Xamarin.Forms Portable) или Пустое приложение (Xamarin.Forms Shared) . Код и полученное приложение будут одинаковыми независимо от выбранного вами шаблона.

В этом примере я буду использовать версию шаблона для Portable Class Library (PCL). Дайте название проекту. Я буду называть решение и первый проект DependencyServiceSample . Затем нажмите кнопку ОК .

Этот процесс создаст три отдельных проекта:

  • DependencyServiceSample — Общая библиотека (PCL)
  • DependencyServiceSample.Android — проект Android
  • DependencyServiceSample.iOS — iOS проект

Xamarin Studio не поддерживает создание проектов Windows Phone. Если вы используете Visual Studio, этот процесс создаст четыре проекта. Он создаст три вышеупомянутых проекта, а также проект Windows Phone с именем DependencyServiceSample.WinPhone .

В общей библиотеке ( DependencyServiceSample ) создайте новый файл интерфейса, назовите его ISampleInterface и дайте ему следующую реализацию:

1
2
3
4
5
6
7
namespace DependencyServiceSample
{
    public interface ISampleInterface
    {
        string GetData();
    }
}

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

Затем мы модифицируем файл App.cs, чтобы использовать DependencyService чтобы получить экземпляр ISampleInterface для использования в приложении Xamarin.Forms. Измените метод GetMainPage чтобы он выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
public static Page GetMainPage ()
       {
           return new ContentPage {
               Content = new Label {
                   Text = DependencyService.Get<ISampleInterface>().GetData(),
                   VerticalOptions = LayoutOptions.CenterAndExpand,
                   HorizontalOptions = LayoutOptions.CenterAndExpand,
               },
           };
       }

Обратите внимание, что единственное отличие состоит в том, что свойство Text Label было изменено на следующую строку:

1
DependencyService.Get<ISampleInterface>().GetData()

Таким образом, вы используете класс DependencyService и универсальный метод Get<>() для извлечения любой реализации ISampleInterface , реализованной в конкретном платформенном проекте, который в данный момент выполняется. Как только этот экземпляр был получен, вы вызываете метод GetData чтобы получить обратно строку и установить свойство Text Label .

Последний шаг состоит из двух частей (три, если вы используете Visual Studio). На этом этапе вам потребуется реализовать интерфейс ISampleInterface во всех проектах, связанных с платформой, в вашем решении.

Начнем с приложения DependencyServiceSample.Android . Все, что вам нужно сделать, это создать новый файл класса в проекте и дать ему любое имя. Я назвал мой Sample_Android . Замените реализацию по умолчанию следующим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
using System;
using DependencyServiceSample.Android;
 
[assembly: Xamarin.Forms.Dependency(typeof(Sample_Android))]
 
namespace DependencyServiceSample.Android
{
    public class Sample_Android : ISampleInterface
    {
        #region ISampleInterface implementation
 
        public string GetData ()
        {
            return «I came from the Android project!»;
        }
 
        #endregion
    }
}

Это простой класс, который реализует интерфейс ISampleInterface и его реализация состоит в том, чтобы просто возвращать string указывающую, что это происходит из проекта Android. Единственное отличие заключается в использовании атрибута assembly в верхней части файла, который регистрирует этот класс с помощью DependencyService чтобы его можно было получить позже.

Теперь давайте создадим еще одну реализацию этого интерфейса в проекте iOS. Создайте новый класс в проекте iOS, назовите его Sample_iOS и замените реализацию по умолчанию следующим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
using System;
using DependencyServiceSample.iOS;
 
[assembly: Xamarin.Forms.Dependency(typeof(Sample_iOS))]
 
namespace DependencyServiceSample.iOS
{
    public class Sample_iOS : ISampleInterface
    {
        #region ISampleInterface implementation
 
        public string GetData ()
        {
            return «I came from the iOS project!»;
        }
 
        #endregion
    }
}

Реализация точно такая же, как версия Android, за исключением того, что она возвращает другую строку, сообщающую, что на этот раз она поступила из проекта iOS. Последний шаг — запустить приложение и посмотреть, получаете ли вы ожидаемый результат.

Вот результат работы приложения для iOS.

Вот результат работы приложения Android.

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

Теперь, когда у вас есть навыки использования DependencyService для получения доступа к нативным функциям из Xamarin.Forms, небо — предел. Вы можете продолжать писать простые реализации, как вы делали в этом руководстве, или вы можете начать использовать более интересные функции платформ.

Одним из наиболее интересных ресурсов для интеграции в DependencyService является раздел « Рецепты » на веб-сайте Xamarin . Здесь вы найдете конкретные реализации платформ для получения доступа к ряду функций, включая:

  • сетей
  • аудио
  • видео
  • геолокации
  • Акселерометры

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

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

Если вы хотите узнать больше о Xamarin, ознакомьтесь с нашим курсом Создание многоплатформенных приложений на C # в Xamarin .

В ходе курса вы узнаете, как создать кроссплатформенное приложение из единой кодовой базы, которая будет работать на трех совершенно разных платформах: iOS, Android и Windows Phone 8. Думаешь, это невозможно? Через некоторое время вы будете делать это самостоятельно. Давай приступим к работе.

Вы можете воспользоваться бесплатной 14-дневной пробной версией подписки Tuts +. Для начала ознакомьтесь с нашими вариантами подписки или, если вы заинтересованы в этом курсе, вы можете приобрести его отдельно за 15 долларов! Вот предварительный просмотр, чтобы вы начали: