Comunicação entre componentes de acoplamento flexível
Gorjeta
Este conteúdo é um excerto do eBook, Enterprise Application Patterns Using .NET MAUI, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
O padrão publicar-assinar é um padrão de mensagens no qual os editores enviam mensagens sem conhecer nenhum destinatário, conhecido como assinante. Da mesma forma, os assinantes ouvem mensagens específicas, sem conhecer nenhum editor.
Os eventos no .NET implementam o padrão publish-subscribe e são a abordagem mais simples para uma camada de comunicação entre componentes se o acoplamento flexível não for necessário, como um controle e a página que o contém. No entanto, os tempos de vida do editor e do assinante são acoplados por referências de objeto entre si, e o tipo de assinante deve ter uma referência ao tipo de editor. Isso pode criar problemas de gerenciamento de memória, especialmente quando há objetos de curta duração que se inscrevem em um evento de um objeto estático ou de longa duração. Se o manipulador de eventos não for removido, o assinante será mantido vivo pela referência a ele no editor, e isso impedirá ou atrasará a coleta de lixo do assinante.
Introdução ao MVVM Toolkit Messenger
A interface do MVVM Toolkit IMessenger
descreve o padrão publish-subscribe, permitindo a comunicação baseada em mensagem entre componentes que são inconvenientes para vincular por objeto e referências de tipo. Este mecanismo permite que editores e assinantes se comuniquem sem ter uma referência direta uns aos outros, ajudando a reduzir as dependências entre componentes, ao mesmo tempo em que permite que os componentes sejam desenvolvidos e testados de forma independente.
Nota
O MVVM Toolkit Messenger faz parte do CommunityToolkit.Mvvm
pacote. Para obter informações sobre como adicionar o pacote ao seu projeto, consulte Introdução ao MVVM Toolkit no Microsoft Developer Center.
Aviso
O .NET MAUI contém uma classe interna MessagingCenter
que não é mais recomendada para uso. Em vez disso, use o MVVM Toolkit Messenger.
A IMessenger
interface permite a funcionalidade de publicação-assinatura multicast. Isso significa que pode haver vários editores que publicam uma única mensagem e pode haver vários assinantes ouvindo a mesma mensagem. A imagem abaixo ilustra essa relação:
Existem duas implementações da IMessenger
interface que acompanham o CommunityToolkit.Mvvm
pacote. O WeakReferenceMessenger
usa referências fracas que podem resultar em limpeza mais fácil para os assinantes de mensagens. Esta é uma boa opção se os seus subscritores não tiverem um ciclo de vida claramente definido. O StrongReferenceMessenger
usa referências fortes que podem resultar em um melhor desempenho e uma vida útil mais claramente controlada da assinatura. Se você tiver um fluxo de trabalho com um tempo de vida muito controlado (por exemplo, uma assinatura vinculada aos métodos e OnDisappearing
páginas OnAppearing
de uma página), a opção pode ser melhor, se o StrongReferenceManager
desempenho for uma preocupação. Ambas as implementações estão disponíveis com implementações padrão prontas para uso fazendo referência a um WeakReferenceMessenger.Default
ou StrongReferenceMessenger.Default
.
Nota
Embora a interface permita a IMessenger
comunicação entre classes de acoplamento flexível, ela não oferece a única solução arquitetônica para esse problema. Por exemplo, a comunicação entre um modelo de exibição e um modo de exibição também pode ser alcançada pelo mecanismo de vinculação e por meio de notificações de alteração de propriedade. Além disso, a comunicação entre dois modelos de visualização também pode ser alcançada através da passagem de dados durante a navegação.
O aplicativo multiplataforma eShop usa a WeakReferenceMessenger
classe para se comunicar entre componentes de acoplamento flexível. O aplicativo define uma única mensagem chamada AddProductMessage
. O AddProductMessage
é publicado pela CatalogViewModel
classe quando um item é adicionado ao carrinho de compras. Em troca, a CatalogView
classe assina a mensagem e usa isso para destacar o produto adicionado com animação em resposta.
No aplicativo multiplataforma eShop, WeakReferenceMessenger
é usado para atualizar a interface do usuário em resposta a uma ação que ocorre em outra classe. Portanto, as mensagens são publicadas a partir do thread em que a classe está executando, com os assinantes recebendo a mensagem no mesmo thread.
Gorjeta
Marshal para a interface do usuário ou thread principal ao executar atualizações da interface do usuário. Se as atualizações das interfaces do usuário não forem feitas nesse thread, isso pode fazer com que o aplicativo falhe ou se torne instável.
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 invocando o MainThread.BeginInvokeOnMainThread
método.
Para obter mais informações sobre Messenger
o , consulte Messenger no Microsoft Developer Center.
Definir uma mensagem
IMessenger
As mensagens são objetos personalizados que fornecem cargas úteis personalizadas. O exemplo de código a seguir mostra a AddProductMessage
mensagem definida no aplicativo multiplataforma eShop:
public class AddProductMessage : ValueChangedMessage<int>
{
public AddProductMessage(int count) : base(count)
{
}
}
A classe base é definida usando ValueChangedMessage<T>
onde T
pode ser de qualquer tipo necessário para passar dados. Tanto os editores de mensagens como os subscritores podem esperar mensagens de um tipo específico (por exemplo, AddProductMessage
). Isso pode ajudar a garantir que ambas as partes concordaram com um contrato de mensagens e que os dados fornecidos com esse contrato serão consistentes. Além disso, essa abordagem fornece segurança de tipo em tempo de compilação e suporte à refatoração.
Publicar uma mensagem
Para publicar uma mensagem, precisaremos usar o IMessenger.Send
método. Isso pode ser acessado mais comumente através de WeakReferenceMessenger.Default.Send
ou StrongReferenceMessenger.Default.Send
. A mensagem enviada pode ser de qualquer tipo de objeto. O exemplo de código a seguir demonstra a publicação da AddProduct
mensagem:
WeakReferenceMessenger.Default.Send(new Messages.AddProductMessage(BadgeCount));
Neste exemplo, o Send
método especifica fornece uma nova instância do objeto para os AddProductMessage
assinantes downstream receberem. Um segundo parâmetro de token adicional pode ser adicionado para usar quando vários assinantes diferentes precisam receber mensagens do mesmo tipo sem receber a mensagem errada.
O Send
método publicará a mensagem e seus dados de carga útil, usando uma abordagem de fogo e esquecimento. Portanto, a mensagem é enviada mesmo que não haja assinantes registrados para receber a mensagem. Nessa situação, a mensagem enviada é ignorada.
Subscrever uma mensagem
Os subscritores podem registar-se para receber uma mensagem utilizando uma das IMessenger.Register<T>
sobrecargas. O exemplo de código a seguir demonstra como o aplicativo multiplataforma eShop assina e processa a AddProductMessage
mensagem:
WeakReferenceMessenger.Default
.Register<CatalogView, Messages.AddProductMessage>(
this,
async (recipient, message) =>
{
await recipient.Dispatcher.DispatchAsync(
async () =>
{
await recipient.badge.ScaleTo(1.2);
await recipient.badge.ScaleTo(1.0);
});
});
No exemplo anterior, o Register
método se inscreve na AddProductMessage
mensagem e executa um delegado de retorno de chamada em resposta ao recebimento da mensagem. Esse delegado de retorno de chamada, especificado como uma expressão lambda, executa o código que atualiza a interface do usuário.
Nota
Evite o uso de dentro do seu delegado de retorno de this
chamada para evitar a captura desse objeto dentro do delegado. Isso pode ajudar a melhorar o desempenho. Em alternativa, utilize o parâmetro recipient
.
Se os dados de carga útil forem fornecidos, 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 carga útil devem ser imutáveis para evitar erros de simultaneidade.
Cancelar a subscrição de uma mensagem
Os subscritores podem cancelar a subscrição de mensagens que já não querem receber. Isso é conseguido com uma das sobrecargas, como demonstrado no exemplo de IMessenger.Unregister
código a seguir:
WeakReferenceMessenger.Default.Unregister<Messages.AddProductMessage>(this);
Nota
Neste exemplo, não é totalmente necessário chamar Unregister
, pois permitirá que objetos WeakReferenceMessenger
não utilizados sejam coletados como lixo. Se o StrongReferenceMessenger
foram usados, seria aconselhável chamar Unregister
para quaisquer assinaturas que não estão mais em uso.
Neste exemplo, a sintaxe do Unsubscribe
método especifica o argumento type da mensagem e o objeto recipient que está escutando mensagens.
Resumo
A interface do MVVM Toolkit IMessenger
descreve o padrão publish-subscribe, permitindo a comunicação baseada em mensagem entre componentes que são inconvenientes para vincular por objeto e referências de tipo. Este mecanismo permite que editores e assinantes se comuniquem sem ter uma referência uns aos outros, ajudando a reduzir as dependências entre componentes, ao mesmo tempo em que permite que os componentes sejam desenvolvidos e testados de forma independente.