Взаимодействие между слабо связанными компонентами
Примечание.
Эта электронная книга была опубликована весной 2017 года и с тех пор не была обновлена. Есть много в книге, которая остается ценным, но некоторые из материалов устарели.
Шаблон "публикация-подписка" — это шаблон обмена сообщениями, в котором издатели отправляют сообщение без знания о получателях, известных как подписчики. Аналогичным образом подписчики прослушивают определенные сообщения, не зная издателей.
События в .NET реализуют шаблон публикации и подписки, который представляет собой наиболее простой подход к обеспечению взаимодействия между компонентами, когда слабая связанность не требуется, как в случае с элементом управления и страницей, на которой он находится. Однако время существования издателя и время существования подписчика связаны посредством ссылок объектов друг на друга, и тип подписчика должен ссылаться на тип издателя. Из-за этого могут возникать проблемы с управлением памятью, особенно если существуют кратковременные объекты, которые подписаны на событие статического или долговременного объекта. Если обработчик событий не удаляется, подписчик продолжает существовать из-за ссылки на него в издателе, вследствие чего сборка мусора для подписчика будет отложена или не произойдет.
Общие сведения о MessagingCenter
Класс Xamarin.FormsMessagingCenter
реализует шаблон публикации и подписки, позволяя обмен данными между компонентами, которые неудобны для связи по ссылкам на объекты и типы. Этот механизм позволяет издателям и подписчикам взаимодействовать без ссылки друг на друга, что помогает сократить зависимости между компонентами, а также позволяет компонентам независимо разрабатывать и тестировать их.
Класс MessagingCenter
предоставляет функцию многоадресной публикации и подписки. Это означает, что может быть несколько издателей, публикующих одно сообщение, и может быть несколько подписчиков, прослушивающих одно и то же сообщение. Рис. 4-1 иллюстрирует эту связь:
Рис. 4-1. Функция многоадресной публикации и подписки
Издатели отправляют сообщения с помощью метода MessagingCenter.Send
, а подписчики прослушивают сообщения с помощью метода MessagingCenter.Subscribe
. Кроме того, подписчики могут также отменять подписку на сообщения, если это необходимо, с помощью метода MessagingCenter.Unsubscribe
.
На внутреннем уровне класс MessagingCenter
использует слабые ссылки. Это означает, что он не будет поддерживать объекты в активном состоянии и позволит им собирать мусор. Поэтому необходимо отменять подписку на сообщения только в том случае, если классу больше не требуется получать сообщения.
Мобильное приложение eShopOnContainers использует MessagingCenter
класс для обмена данными между слабо связанных компонентов. Приложение определяет три сообщения:
- Сообщение
AddProduct
публикуется классомCatalogViewModel
при добавлении элемента в корзину покупок. В свою очередь,BasketViewModel
класс подписывается на сообщение и увеличивает количество элементов в корзине покупок в ответе. Кроме того,BasketViewModel
класс также отменяет подписку из этого сообщения. - Сообщение
Filter
публикуется классомCatalogViewModel
, когда пользователь применяет фильтр фирменной символики или типа к элементам, отображаемым из каталога. В свою очередь,CatalogView
класс подписывается на сообщение и обновляет пользовательский интерфейс таким образом, чтобы отображались только элементы, соответствующие критериям фильтра. - Сообщение
ChangeTab
публикуется классомMainViewModel
приCheckoutViewModel
переходе кMainViewModel
следующему успешному созданию и отправке нового заказа. В свою очередьMainView
, класс подписывается на сообщение и обновляет пользовательский интерфейс, чтобы вкладка "Мой профиль " активна для отображения заказов пользователя.
Примечание.
Хотя MessagingCenter
класс разрешает обмен данными между слабо связанных классов, он не предлагает единственное архитектурное решение для этой проблемы. Например, взаимодействие между моделью представления и представлением также может быть достигнуто подсистемой привязки и уведомлениями об изменении свойств. Кроме того, взаимодействие между двумя моделями представления также можно добиться путем передачи данных во время навигации.
В мобильном приложении MessagingCenter
eShopOnContainers используется для обновления пользовательского интерфейса в ответ на действие, выполняемое в другом классе. Таким образом, сообщения публикуются в потоке пользовательского интерфейса, при этом подписчики получают сообщение в одном потоке.
Совет
Маршалирование в поток пользовательского интерфейса при выполнении обновлений пользовательского интерфейса. Если для обновления пользовательского интерфейса требуется сообщение, отправленное из фонового потока, обработайте сообщение в потоке пользовательского интерфейса в подписчике, вызвав Device.BeginInvokeOnMainThread
метод.
Дополнительные сведения см. в MessagingCenter
разделе MessagingCenter.
Определение сообщения
MessagingCenter
сообщения — это строки, используемые для идентификации сообщений. В следующем примере кода показаны сообщения, определенные в мобильном приложении eShopOnContainers:
public class MessageKeys
{
// Add product to basket
public const string AddProduct = "AddProduct";
// Filter
public const string Filter = "Filter";
// Change selected Tab programmatically
public const string ChangeTab = "ChangeTab";
}
В этом примере сообщения определяются с помощью констант. Преимущество этого подхода заключается в том, что он обеспечивает безопасность типов компиляции и поддержку рефакторинга.
Публикация сообщения
Издатели уведомляют подписчиков о сообщении с помощью одной из перегрузок MessagingCenter.Send
. В следующем примере кода демонстрируется публикация AddProduct
сообщения:
MessagingCenter.Send(this, MessageKeys.AddProduct, catalogItem);
В этом примере Send
метод задает три аргумента:
- Первый аргумент указывает класс отправителя. Класс отправителя должен быть указан любым подписчиком, желающим получить сообщение.
- Второй аргумент указывает само сообщение.
- Третий аргумент указывает полезные данные для отправки подписчику. В этом случае полезные данные являются экземпляром
CatalogItem
.
Метод Send
будет публиковать сообщение и его полезные данные, используя подход fire-and-forget. Поэтому сообщение отправляется, даже если отсутствуют подписчики, зарегистрированные для получения сообщения. В этом случае отправленное сообщение игнорируется.
Примечание.
Метод MessagingCenter.Send
может использовать универсальные параметры для управления доставкой сообщений. Таким образом, несколько сообщений, которые совместно используют удостоверение сообщения, но отправляют различные типы данных полезных данных, могут быть получены различными подписчиками.
Подписка на сообщение
Подписчики могут зарегистрироваться для получения сообщения с помощью одной из перегрузок MessagingCenter.Subscribe
. В следующем примере кода показано, как мобильное приложение eShopOnContainers подписывается на и обрабатывает AddProduct
сообщение:
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(
this, MessageKeys.AddProduct, async (sender, arg) =>
{
BadgeCount++;
await AddCatalogItemAsync(arg);
});
В этом примере Subscribe
метод подписывается на AddProduct
сообщение и выполняет делегат обратного вызова в ответ на получение сообщения. Этот делегат обратного вызова, указанный как лямбда-выражение, выполняет код, обновляющий пользовательский интерфейс.
Совет
Рекомендуется использовать неизменяемые полезные данные. Не пытайтесь изменить полезные данные из делегата обратного вызова, так как несколько потоков могут одновременно получать доступ к полученным данным. В этом сценарии данные полезных данных должны быть неизменяемыми, чтобы избежать ошибок параллелизма.
Подписчику может не потребоваться обрабатывать каждый экземпляр опубликованного сообщения, и это можно контролировать с помощью аргументов универсального типа, указанных в методе Subscribe
. В этом примере подписчик получит AddProduct
только сообщения, отправляемые из CatalogViewModel
класса, полезные данные которых являются экземпляром CatalogItem
.
Отмена подписки из сообщения
Если подписчики больше не должны получать сообщения, можно отменить подписку на них. Это достигается с одной из MessagingCenter.Unsubscribe
перегрузок, как показано в следующем примере кода:
MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct);
В этом примере синтаксис метода отражает аргументы типа, Unsubscribe
указанные при подписке на получение AddProduct
сообщения.
Итоги
Класс Xamarin.FormsMessagingCenter
реализует шаблон публикации и подписки, позволяя обмен данными между компонентами, которые неудобны для связи по ссылкам на объекты и типы. Этот механизм позволяет издателям и подписчикам взаимодействовать без ссылки друг на друга, что помогает сократить зависимости между компонентами, а также позволяет компонентам независимо разрабатывать и тестировать их.