.NET MAUI - MVVM Messenger

2022. 8. 16. 00:00MAUI

반응형

IMessenger 인터페이스는 서로 다른 개체 간에 메시지를 교환하는 데 사용할 수 있다.

강력한 참조를 유지할 필요 없이 애플리케이션의 모듈을 분리하는 데 유용하다.

MVVM 툴킷은  WeakReferenceMessenger 및 StrongReferenceMessenger를 제공한다.

WeakReferenceMessenger는 내부적으로 약한 참조를 사용하여 자동으로 메모리를 관리한다.

StrongReferenceMessenger는 강력한 참조를 사용하고 필요하지 않을 때 직접 구독 취소해야한다.

하지만 더 나은 성능과 훨씬 적은 메모리 사용량을 제공한다.

 

특정 통신 채널(각각 고유한 토큰으로 식별됨)을 통해 메시지를 보내는 것도 가능하므로 여러 모듈이 충돌을 일으키지 않고 동일한 유형의 메시지를 교환할 수 있다. 토큰 없이 보낸 메시지는 기본 공유 채널을 사용한다.

 

메시지 등록을 수행하는 방법

 

  • IRecipient<TMessage> 인터페이스 사용
    RegisterAll 호출로 모든 처리기를 등록. 선언된 모든 메시지 처리기의 수신자를 자동으로 등록
  • MessageHandler<TRecipient, TMessage> 대리자를 사용
    더 많은 유연성이 필요하거나 간단한 람다 식을 사용하려는 경우에 유용

 

WeakReferenceMessenger와 StrongReferenceMessenger는 모두 패키지에 내장된 스레드로부터 안전한 구현을 제공하는 Default 속성도 노출한다. 필요한 경우 여러 메신저 인스턴스를 생성하는 것도 가능하다.

 

WeakReferenceMessenger 유형은 사용이 더 간단하고 MvvmLight 라이브러리의 메신저 유형 동작과 일치하므로 MVVM Toolkit에서 ObservableRecipient 유형이 사용하는 기본 유형이다. StrongReferenceType은 해당 클래스의 생성자에 인스턴스를 전달하여 계속 사용할 수 있다.

 

메시지 생성

public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
    public LoggedInUserChangedMessage(User user) : base(user)
    {        
    }
}

특정 모듈에서 메세지 등록

WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
{
    //여기에서 메시지를 처리합니다. 여기서 r은 수신자이고 m은 입력 메시지입니다. 
    //입력으로 전달된 수신자를 사용하면 람다식이 "this"를 캡처하지 않도록 하여 성능이 향상됩니다.
});

메세지 전송

WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

 

메세지 수신 제거

// 기본채널에서 LoggedInUserChangedMessage 에 대한 수신을 취소합니다
WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage>(this);

// 지정된 채널에서(42) LoggedInUserChangedMessage 에 대한 수신을 취소합니다.
WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage, int>(this, 42);

// 모든 채널의 LoggedInUserChangedMessage 에 대한 수신을 취소합니다.
WeakReferenceMessenger.Default.UnregisterAll(this);

앞에서 언급했듯이 WeakReferenceMessenger 유형을 사용할 때는 약한 참조를 사용하여 수신자를 추적하기 때문에 꼭 필요한 것은 아니다. 즉, 사용하지 않는 수신자는 활성 메시지 핸들러가 있더라도 여전히 가비지 수집 대상이 된다. 그래도 성능을 향상시키려면 구독을 취소하는 것이 좋다. 반면에 StrongReferenceMessenger 구현은 강력한 참조를 사용하여 등록된 수신자를 추적한다. 이는 성능상의 이유로 수행되며 메모리 누수를 방지하려면 등록된 각 수신자를 수동으로 등록 취소해야 한다. 즉, 수신자가 등록되어 있는 한 사용 중인StrongReferenceMessenger 인스턴스는 이에 대한 활성 참조를 유지하므로 가비지 수집기가 해당 인스턴스를 수집할 수 없다. 수동으로 처리하거나 ObservableRecipient에서 상속할 수 있다. ObservableRecipient는 기본적으로 비활성화될 때 받는 사람에 대한 모든 메시지 등록을 자동으로 제거한다(자세한 내용은 ObservableRecipient에 대한 문서 참조).

 

IRecipient<TMessage> 인터페이스를 사용하여 메시지 처리기를 등록하는 것도 가능하다.

이 경우 각 수신자는 지정된 메시지 유형에 대한 인터페이스를 구현하고

메시지를 수신할 때 호출될 Receive(TMessage) 메서드를 제공해야 한다.

// Create a message
public class MyRecipient : IRecipient<LoggedInUserChangedMessage>
{
    public void Receive(LoggedInUserChangedMessage message)
    {
        // Handle the message here...   
    }
}

// Register that specific message...
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this);

// ...or alternatively, register all declared handlers
WeakReferenceMessenger.Default.RegisterAll(this);

// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

 

RequestMessage 사용

send 후 응답이 필요한 경우 RequestMessage<T> 를 상속한 class 를 생성하여

.Reply 를 통해 필요한 응답을 전달 한다. 

// Create a message
public class LoggedInUserRequestMessage : RequestMessage<User>
{
}

// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    // Assume that "CurrentUser" is a private member in our viewmodel.
    // As before, we're accessing it through the recipient passed as
    // input to the handler, to avoid capturing "this" in the delegate.
    m.Reply(r.CurrentUser);
});

// Request the value from another module
User user = WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();

현재 유저에 대한 정보를 가져오기 위해 위와 같이 코드를 구성했다. 

send 이후 m.Reply 를 통해 CurrentUser 를 Send 한 곳에 응답으로 보내주고 있다. 

 

Aysnc 모델도 존재한다. 

// Create a message
public class LoggedInUserRequestMessage : AsyncRequestMessage<User>
{
}

// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    m.Reply(r.GetCurrentUserAsync()); // We're replying with a Task<User>
});

// Request the value from another module (we can directly await on the request)
User user = await WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();

Task<User> 로 전달 되고  await 을 통해 User 로 받을 수 있다. 

 

 

관련영상

https://youtu.be/gsdyDDpuDWo

 

 

반응형

'MAUI' 카테고리의 다른 글

.NET MAUI - MVVM Dependency Injection 1/2  (0) 2022.08.18
.NET MAUI - MVVM SourceGenerator  (0) 2022.08.17
.NET MAUI - MVVM Command  (0) 2022.08.15
.NET MAUI - MVVM ObservableValidator  (0) 2022.08.12
.NET MAUI - MVVM ObservableRecipient  (0) 2022.08.11