Comunicação entre componentes flexíveis
Observação
Este e-book foi publicado na primavera de 2017 e não foi atualizado desde então. Há muito no livro que permanece valioso, mas parte do material está desatualizado.
O padrão de publicação-assinatura é um padrão de troca de mensagens em que os publicadores enviam mensagens sem ter conhecimento de nenhum receptor, conhecido como assinante. Da mesma forma, os assinantes escutam mensagens específicas, sem ter conhecimento de nenhum publicador.
Os eventos no .NET implementam o padrão de publicação-assinatura, e são a abordagem mais simples e direta para uma camada de comunicação entre os componentes se o acoplamento solto não é necessário, como um controle e a página que o contém. No entanto, os tempos de vida do publicador e do assinante são acoplados por referências de objeto entre si e o tipo de assinante deve ter uma referência para o tipo de publicador. Isso pode criar problemas de gerenciamento de memória, especialmente quando há objetos de curta duração que assinam o evento de um objeto estático ou de longa duração. Se o manipulador de eventos não for removido, o assinante será mantido ativo pela referência a ele no publicador e isso impedirá ou atrasará a coleta de lixo do assinante.
Introdução ao MessagingCenter
A Xamarin.FormsMessagingCenter
classe implementa o padrão de publicação-assinatura, permitindo a comunicação baseada em mensagens entre componentes que são inconvenientes para vincular por referências de objeto e tipo. Esse mecanismo permite que publicadores e assinantes se comuniquem sem uma referência entre eles, ajudando a reduzir as dependências entre os componentes, permitindo também que os componentes sejam desenvolvidos e testados independentemente.
A classe MessagingCenter
fornece a funcionalidade de publicação-assinatura multicast. Isso significa que pode haver vários publicadores que publicam uma única mensagem e pode haver vários assinantes ouvindo a mesma mensagem. A Figura 4-1 ilustra essa relação:
Figura 4-1: Funcionalidade de publicação/assinatura multicast
Os publicadores enviam mensagens usando o método MessagingCenter.Send
, enquanto os assinantes ouvem mensagens usando o método MessagingCenter.Subscribe
. Além disso, os assinantes também podem cancelar a assinatura de mensagens, se necessário, usando o método MessagingCenter.Unsubscribe
.
Internamente, a classe MessagingCenter
usa referências fracas. Isso significa que ela não manterá os objetos ativos e permitirá que eles sejam coletados como lixo. Portanto, só deverá ser necessário cancelar a assinatura de uma mensagem quando uma classe não desejar mais recebê-la.
O aplicativo móvel eShopOnContainers usa a MessagingCenter
classe para se comunicar entre componentes fracamente acoplados. O aplicativo define três mensagens:
- A mensagem
AddProduct
é publicada pela classeCatalogViewModel
quando um item é adicionado à cesta de compras. Em troca, aBasketViewModel
classe assina a mensagem e incrementa o número de itens na cesta de compras em resposta. Além disso, a classe também cancelaBasketViewModel
a assinatura dessa mensagem. - A
Filter
mensagem é publicada pelaCatalogViewModel
classe quando o usuário aplica um filtro de marca ou tipo aos itens exibidos no catálogo. Em troca, aCatalogView
classe assina a mensagem e atualiza a interface do usuário para que apenas os itens que correspondem aos critérios de filtro sejam exibidos. - A
ChangeTab
mensagem é publicada pelaMainViewModel
classe quando aCheckoutViewModel
navegação para oMainViewModel
seguinte a criação e o envio bem-sucedidos de um novo pedido. Em troca, aMainView
classe assina a mensagem e atualiza a interface do usuário para que a guia Meu perfil esteja ativa, para mostrar os pedidos do usuário.
Observação
Embora a classe MessagingCenter
permita a comunicação entre classes pouco acopladas, ela não é a única solução arquitetônica para esse problema. Por exemplo, a comunicação entre um modelo de exibição e uma exibição também pode ser obtida pelo mecanismo de associação e por meio de notificações de alteração de propriedade. Além disso, a comunicação entre dois modelos de exibição também pode ser obtida passando dados durante a navegação.
No aplicativo móvel eShopOnContainers, MessagingCenter
é usado para atualizar na interface do usuário em resposta a uma ação que ocorre em outra classe. Portanto, as mensagens são publicadas no thread da interface do usuário, com os assinantes recebendo a mensagem no mesmo thread.
Dica
Marechal para o thread da interface do usuário ao executar atualizações da interface do usuário. Se uma mensagem enviada de um thread em segundo plano for necessária para atualizar a interface do usuário, processe a mensagem no thread da interface do usuário no assinante chamando o método Device.BeginInvokeOnMainThread
.
Para obter mais informações sobre MessagingCenter
o , consulte MessagingCenter.
Definindo uma mensagem
MessagingCenter
as mensagens são cadeias de caracteres usadas para identificar mensagens. O exemplo de código a seguir mostra as mensagens definidas no aplicativo móvel 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";
}
Neste exemplo, as mensagens são definidas usando constantes. A vantagem dessa abordagem é que ela fornece suporte para a segurança do tipo durante a compilação e a refatoração.
Publicando uma mensagem
Os publicadores notificam os assinantes de uma mensagem com uma das sobrecargas MessagingCenter.Send
. O exemplo de código a seguir demonstra a publicação da mensagem AddProduct
:
MessagingCenter.Send(this, MessageKeys.AddProduct, catalogItem);
Neste exemplo, o Send
método especifica três argumentos:
- O primeiro argumento especifica a classe do remetente. A classe do remetente deve ser especificada por quaisquer assinantes que desejam receber a mensagem.
- O segundo argumento especifica a mensagem.
- O terceiro argumento especifica os dados de carga a serem enviados ao assinante. Nesse caso, os dados de carga útil são uma
CatalogItem
instância.
O método Send
publicará a mensagem, bem como os dados de conteúdo, usando uma abordagem do tipo “disparar e esquecer”. Portanto, a mensagem é enviada mesmo quando não há assinantes registrados para recebê-la. Nessa situação, a mensagem enviada é ignorada.
Observação
O método MessagingCenter.Send
pode usar parâmetros genéricos para controlar como as mensagens são entregues. Portanto, várias mensagens que compartilham uma identidade de mensagem, mas enviam tipos de dados de conteúdo diferentes, podem ser recebidas por diferentes assinantes.
Assinando uma mensagem
Os assinantes podem se registrar para receber uma mensagem usando uma das sobrecargas MessagingCenter.Subscribe
. O exemplo de código a seguir demonstra como o aplicativo móvel eShopOnContainers assina e processa a AddProduct
mensagem:
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(
this, MessageKeys.AddProduct, async (sender, arg) =>
{
BadgeCount++;
await AddCatalogItemAsync(arg);
});
Neste exemplo, o Subscribe
método assina a AddProduct
mensagem e executa um delegado de retorno de chamada em resposta ao recebimento da mensagem. Esse representante de retorno de chamada, especificado como uma expressão lambda, executa o código que atualiza a interface do usuário.
Dica
Considere o uso de dados de carga imutáveis. Não tente modificar os dados de carga de dentro de um delegado de retorno de chamada porque vários threads podem estar acessando os dados recebidos simultaneamente. Nesse cenário, os dados de conteúdo devem ser imutáveis para evitar erros de simultaneidade.
Um assinante pode não precisar manipular todas as instâncias de uma mensagem publicada, e isso pode ser controlado pelos argumentos de tipo genérico especificados no método Subscribe
. Neste exemplo, o assinante receberá AddProduct
apenas mensagens enviadas da CatalogViewModel
classe, cujos dados de carga útil são uma CatalogItem
instância.
Cancelar a assinatura de uma mensagem
Os assinantes podem cancelar a assinatura de mensagens que não desejam mais receber. Isso é feito com uma das sobrecargas MessagingCenter.Unsubscribe
, conforme demonstrado no seguinte exemplo de código:
MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct);
Neste exemplo, a sintaxe do Unsubscribe
método reflete os argumentos de tipo especificados ao se inscrever para receber a AddProduct
mensagem.
Resumo
A Xamarin.FormsMessagingCenter
classe implementa o padrão de publicação-assinatura, permitindo a comunicação baseada em mensagens entre componentes que são inconvenientes para vincular por referências de objeto e tipo. Esse mecanismo permite que publicadores e assinantes se comuniquem sem uma referência entre eles, ajudando a reduzir as dependências entre os componentes, permitindo também que os componentes sejam desenvolvidos e testados independentemente.