1. Установка сцены
Когда речь заходит о написании мобильных приложений, важно интегрировать их с конкретными функциями платформы, которые доступны вам, когда это имеет смысл. Например, если вы пишете навигационное приложение, для вас будет иметь смысл использовать геолокационные функции устройства и платформы. Если бы вы создавали приложение, чтобы помочь людям с нарушениями зрения, вы бы хотели интегрировать их с любыми доступными функциями преобразования текста в речь.
Именно разработчики, использующие эти функции, отличают себя и свои приложения от остальных. Эти простые вещи принимают обычное приложение и делают его великолепным. Но что произойдет, если вы захотите воспользоваться этими функциями, но решили использовать Xamarin.Forms в качестве своего кроссплатформенного механизма выбора? Нужно ли терять надежду на эти функции только потому, что вы решили, что ваше приложение должно быть кроссплатформенным, и вы хотите иметь возможность делиться как можно большим количеством логики и кода пользовательского интерфейса? Точно нет.
Эти типы вопросов неизбежно вызывают некоторые проблемы для разработчиков, которые принимают новые технологии, такие как Xamarin.Forms. До выпуска Xamarin.Forms, когда вы работали непосредственно с шаблонами проектов Xamarin.iOS, Xamarin.Android и Windows Phone, доступ к этим типам функций был довольно простым. С точки зрения Xamarin, если бы вы могли найти образец C # — или даже документацию на родном языке и SDK — для конкретной функции, вы могли бы просто сопоставить свой код с нативными концепциями, потому что Xamarin проделал такую потрясающую работу по переводу тех же нативных концепций на эти платформы в конструкции языка C #. Функции Windows Phone были еще проще, потому что перевод не требовался. Все, что вам нужно было сделать, это прочитать документацию.
К счастью для нас, разработчиков, Xamarin потратил много времени и усилий на разработку механизма доступа к этим же функциям, даже если мы решим использовать их уровень абстракции Xamarin.Forms. Этот механизм известен как DependencyService
.
2. Обзор DependencyService
На первый взгляд, такое имя, как DependencyService
может показаться немного пугающим. Это звучит как некоторая причудливая терминология программирования, которую понимают лишь немногие элиты. Если вы когда-либо работали с контейнерами Dependency Injection (DI) или Inversion of Controller (IoC), вы должны чувствовать себя как дома с DependencyService
. Если нет, уверяю вас, это очень простая концепция для понимания, если разбить ее на составляющие.
Что такое DependencyService
?
По своей сути DependencyService
является классом. Это класс, единственная цель существования которого — позволить вам зарегистрировать любое количество классов в вашем приложении. Под регистрацией я имею в виду взять любой класс, который у вас есть, и сообщить об этом службе. Как только DependencyService
узнает о классе, он может пойти и извлечь экземпляр этого класса при необходимости. Это еще одна цель DependencyService
. Если в какой-то момент в вашем приложении вы решите, что вам нужен экземпляр класса, который был зарегистрирован в DependencyService
, вы можете запросить или получить его экземпляр.
Когда вы действительно погрузитесь в основные моменты DependencyService
, это очень широкое обобщение. Но с точки зрения разработчика, это почти все, что вам нужно знать. Однако есть еще одна концепция, о которой вам необходимо знать при работе с интерфейсами DependencyService
. Когда дело доходит до DependencyService
и всей этой регистрации и извлечения, вы обычно делаете это в отношении интерфейсов. Это означает, что когда вы регистрируете класс, вы регистрируете его как реализацию определенного интерфейса. И когда вы извлекаете класс, вы фактически запрашиваете у DependencyService
реализацию этого интерфейса. На данный момент вам не важно, что такое реализация, вы просто хотите класс, реализующий этот интерфейс.
Как работает DependencyService
?
Теперь, когда у вас есть базовое понимание на концептуальном уровне, что такое DependencyService
, давайте немного углубимся и посмотрим, как оно на самом деле работает.
Чтобы использовать DependencyService
, вам нужны три вещи:
- Интерфейсы: Интерфейс — это просто конструкция, которая определяет, какие члены должны присутствовать в любом классе, который решит реализовать или согласиться с этим контрактом.
- Регистрация: Регистрация — это всего лишь механизм, позволяющий
DependencyService
знать, что определенный класс желает быть зарегистрированным и может быть получен позднее. - Расположение: Эта концепция часто ассоциируется с шаблоном в разработке программного обеспечения, известным как шаблон Service Locator . Это просто означает, что вы можете перейти в одно место,
DependencyService
, и запросить некоторую функциональность, класс, без необходимости непосредственного создания нового экземпляра.
Давайте углубимся в каждое из этих понятий чуть более подробно.
3. Интерфейсы
В наши дни интерфейсы очень распространены в большинстве языков объектно-ориентированного программирования (ООП). Использование интерфейса позволяет вам определять контракт, который содержит ряд свойств, методов, событий и т. Д., Которые должны быть реализованы любым классом, который соглашается с этим контрактом.
Вот очень простой пример интерфейса и класса, который реализует этот интерфейс.
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
.
4. Регистрация
В контексте 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
мог его создать. В моем примере выше я не определил конструктор, поэтому компилятор по умолчанию создаст для меня конструктор без параметров.
5. Местоположение
Последняя часть головоломки — получение зарегистрированного класса. Это на самом деле самая легкая часть всего процесса. Чтобы извлечь экземпляр зарегистрированного класса, вы просто используете класс 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
.
6. Использование DependencyService
Теперь, когда у вас есть концептуальное понимание того, что такое 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 вы теперь можете напрямую погрузиться в проекты, специфичные для платформы, чтобы получить доступ к собственному коду.
7. Куда пойти отсюда
Теперь, когда у вас есть навыки использования DependencyService
для получения доступа к нативным функциям из Xamarin.Forms, небо — предел. Вы можете продолжать писать простые реализации, как вы делали в этом руководстве, или вы можете начать использовать более интересные функции платформ.
Одним из наиболее интересных ресурсов для интеграции в DependencyService
является раздел « Рецепты » на веб-сайте Xamarin . Здесь вы найдете конкретные реализации платформ для получения доступа к ряду функций, включая:
- сетей
- аудио
- видео
- геолокации
- Акселерометры
Все эти функции находятся в вашем распоряжении, когда речь идет о приложениях Xamarin.Forms. С DependencyService
эти функции могут быть вызваны в любой момент.
Вывод
Теперь, когда вы знаете и понимаете DependencyService
, вам больше не нужно бояться доступа к специфическим функциям платформы из приложения Xamarin.Forms. Теперь у вас есть инструменты, которые позволяют вам использовать эти удивительные встроенные функции устройств, которые в конечном итоге позволят вам отличать свои приложения от остальных в магазинах приложений.
Следующий шаг: смотреть курс
Если вы хотите узнать больше о Xamarin, ознакомьтесь с нашим курсом Создание многоплатформенных приложений на C # в Xamarin .
В ходе курса вы узнаете, как создать кроссплатформенное приложение из единой кодовой базы, которая будет работать на трех совершенно разных платформах: iOS, Android и Windows Phone 8. Думаешь, это невозможно? Через некоторое время вы будете делать это самостоятельно. Давай приступим к работе.
Вы можете воспользоваться бесплатной 14-дневной пробной версией подписки Tuts +. Для начала ознакомьтесь с нашими вариантами подписки или, если вы заинтересованы в этом курсе, вы можете приобрести его отдельно за 15 долларов! Вот предварительный просмотр, чтобы вы начали: