-
Be Book Application Kit Application Kit Indeks

Komunikacja

Application Kit dostarcza systemu przekazywania komunikatów, który pozwala Twojej aplikacji wysyłać i odbierać komunikaty z innych aplikacji (włącznie z serwerami aplikacjami zdefiniowanymi przez Be) i z innych wątków w Twojej własnej aplikacji.

Najważniejszymi klasami komunikacyjnymi są:

Innymi klasami komunikacyjnymi są:

Reszta tego rozdziału spogląda na...

opisując jak klasy są dopasowane do siebie w systemie rozgłaszania komunikatów z uwypukleniem tego co możesz zrobić w swojej aplikacji aby wziąć w tym udział.


Cechy klas podstawowych

Łącznie patrząc, te cztery podstawowe klasy komunikacji obejmują ogromny fragment API. Na szczęście, podstawowa część tego API jest dość mała; właśnie na to będziemy tutaj spoglądali.

The BMessage Class

W klasie BMessage, jest jedna podstawowa dana członkowska i dwie podstawowe funkcje:

/* Argumentami są: name, index, value (zwracane przez referencję) */
bool returnValue;
aMessage.FindBool("IsEnabled", 2, &returnValue);

Podsumowując, BMessage zawiera (1) stałą polecenia i (2) zbiór pól danych. Każdy BMessage który jest używany w systemie komunikacji musi mieć stałą polecenia ale nie każdy obiekt potrzebuje mieć pola danych. (Inne części BeOS'a używają obiekty BMessage tylko dla ich danych. Obiekt BClipboard na przykład, ignoruje stałą polecenia BMessage'a.)

Kiedy omawiamy generowane przez system obiekty BMessage, odnosimy się do tych obiektów przez ich stałą polecenia. Przykładowo, "B_MOUSE_DOWN" znaczy "BMessage, który ma B_MOUSE_DOWN jako jego stałą polecenia".

Zauważ, że BMessage nie wie jak wysłać się samemu. Jednak jak zobaczymy później, wie on jak odpowiedzieć swojemu nadawcy, że dotarł on do rąk odbiorcy.

Klasa BLooper

Rolą BLooper'a jest jest otrzymywanie komunikatu i rozpracowanie tego, co z nim zrobić. Są cztery części tego zadania, ucieleśnionego w tych funkcjach:

/* Ta forma konstruktora BMessage ustawia stałą polecenia. */
BMessage *myMessage = new BMessage(YOUR_CONSTANT_HERE);
be_app->PostMessage(myMessage)

Klasy BApplication i BWindow dziedziczą z BLooper'a.

Klasa BHandler

Obiekty BHandler są wywoływane przy obsłudze komunikatów, które odbiera BLooper. BHandler zależy od dwóch zasadniczych funkcji:

void MyHandler::MessageReceived(BMessage *message)
{
   /* Sprawdza stałą polecenia. */
   switch ( message->what ) {

   case YOUR_CONSTANT_HERE:
      /* Wywołuje funkcję, która obsługuje to sortowanie komunikatów. */
      HandlerFunctionA();
      break;

   case ANOTHER_CONSTANT_HERE:
      /* jak wyżej */
      HandlerFunctionB();
      break;
   
   default:
      /* Komunikaty których Twoja klasa nie rozpoznaje muszą być
      * przekazane do implementacji klasy podstawowej. */
      baseClass::MessageReceived(message);
      break;
   }
}

BLooper dziedziczy z BHandler, i automatycznie dodaje siebie do swojego własnego łańcucha handlerów. To znaczy, że BLooper może obsługiwać komunikaty które on odbiera - jest to domyślne zachowanie dla większości komunikatów. Będziemy badać to zagadnienie o wiele dalej w tym rozdziale.

Innymi klasami które dziedziczą z BHandler'a są BView i BShelf (obie w Interface Kit).

Klasa BMessenger

Najważniejszą cechą BMessenger'a jest to, że może on wysyłać komunikaty do aplikacji zdalnej. Aby to zrobić, podejmuje on dwa kroki, które wskazują najwazniejsze cechy klasy:

BMessenger'y mogą również być użyte do adresowania lokalnej pary looper/handler.


Od looper'a do handler'a

BLooper pobiera komunikat ze swojej kolejki komunikatów i wewnątrz swojej DispatchMessage() funkcji wysyła komunikat przez wywołanie funkcji BHandler'a. Ale (1) który BHandler i (2) która funkcja?

Znajdowanie handler'a

Najpierw, odpowiedzmy na pytanie "który BHandler". Do określenia który BHandler powinien obsłużyć nadchodzący komunikat, BLooper postępuje według tych kroków:

1.    Czy adresat BMessage określa BHandler'a? Obie funkcje BLooper::PostMessage() i BMessenger::SendMessage() dostarczja dodatkowych argumentów, które pozwola CI określić adresata handler'a którego chcesz mieć do obsłużenia komunikatu (możesz również ustawić adresata w konstruktorze BMessenger'a). Jeśli BHandler jest określony, BMessage będzie się pojawiał funkcji MessageReceived() tego obiektu (lub będzie on wywoływał funkcję przechwytującą mapowania komunikatu, jak jest wyjaśnione poniżej).

2.    Czy BLooper wyznaczył preferowanego handler'a? Poprzez swoją funkcję SetPreferredHandler(), BLooper może wyznaczyć jeden z obiektów w swoim łańcuchu handlerów jako swój preferowany handler i przekazać wszystkie komunikaty do tego obiektu.

3.    BLooper obsługuje BMessage sam. Jeśli nie ma wyznaczonego docelowego handler'a lub nie jest wyznaczony preferowany handler, BLooper obsługuje komunikat sam - innymi słowy, komunikat jest przekazywany do BLooper'a własną funkcją MessageReceived() (lub funkcją mapowania komunikatu).

Powinniśmy wspomnieć tutaj, że obie klasy BApplication i BWindow dobrze zestawiają ten kawałek procesu decyzyjnego. Jednak wtrącają się one tylko stosowania komunikatów systemowych (opisanych poniżej). Komunikaty które definijesz sam (tj. stałe poleceń które są określone w Twojej aplikacji) będą zawsze śledzić komunikat wysyłany ścieżką opisaną tutaj.

Jeśli popatrzysz na protokół DispatchMessage(), zauważysz, że ma on BMessage i BHandler jako argumenty. Innymi słowy, opisywana tutaj decyzja, "który handler" jest w rzeczywistości wykonywana zanim zostanie wywołana funkcja DispatchMessage(). Ogólnie, jest to implementacja szczegółu którym nie musisz się martwić. Jeśli chcesz to myśl, że tą decyzję podejmuje DispatchMessage() - my nic nie zrobiliśmy, żeby wyprowadzić Cię z tego błędnego pojęcia - idź dalej i myśl tak.

Znajdowanie funkcji

Jak napisano powyższej, BLooper przekazuje BMessage do BHandler'a przez by wywołanie końcowej funkcji MessageReceived(). Jest to prawdą dla wszystkich komunikatów, które tworzysz sam, ale nie jest to prawdziwe dla pewnych komunikatów, które definiuje i wysyła system. Te wygenerowane przez system komunikaty (lub komunikaty systemowe) - szczególnie te, które zgłaszają zdarzenia użytkownika takie jak B_MOUSE_DOWN lub B_APP_ACTIVATED - wywołują określone funkcje przechwytujące.

Dla przykłądu, gdy użytkownik naciśnie klawisz, komunikat B_KEY_DOWN jest wysyłany do aktywnego obiektu BWindow. Z wnętrza jego funkcji DispatchMessage(), BWindow wywołuje funkcję MouseDown() obiektu BView która aktualnie posiada ognisko klawiatury. (Kiedy BView uzyskał ognisko zdarzeń klawiatury, jego okno wyznacza go aby był preferowanym handler'em.)

Więc pytanie z "której funkcji" jest zupełnie proste: Jeżeli BMessage jest komunikatem systemowym, to jest mapowany do funkcji przechwytującej, wywoływana jest funkcja przechwytująca. Jeśli nie jest ona mapowana do przechwytującej, jest wywoływana funkcja MessageReceived() BHandler'a.

A full list of system messages and the hook functions that they're mapped to is given in the Dodatku Komunikatów Systemowych (System Messages Appendix). Zauważ, że nie wszystkie komunikaty systemowe są mapowane, by funkcje przechwytujące; część z nich pokazano w MessageReceived().

Dziedziczenie i łańcuch handler'a

Popatrzmy na MessageReceived() znowu. Zostało to podkreślone w skrawku kodu pokazanym wcześniej, że typowa implementacjaMessageReceived() powinna zawierać wywołanie podstawowej wersji funkcji klasy:

void MyHandler::MessageReceived(BMessage *message)
{
   switch ( message->what ) {

   /* Stałe poleceń, które Ty obsługujesz przebiegają tutaj. */   

   default:
      /* Komunikaty, których Twoja klasa nie rozpoznaje muszą być
       * przekazane do implementacji klasy podstawowej. */
      baseClass::MessageReceived(message);
      break;
   }
}

To nie jest tylko dobry pomysł - to jest zasadnicza część systemu komunikowania się. Przesyłanie komunikatów do klasy podstawowej załatwia dwie rzeczy: pozwala ono (1) na przekazywanie komunikatów w górę hierarchii klas i (2) przekazywanie ich wzdłuż łańcucha handler'a (w tej kolejności).

Przekazywanie w górę hierarchii klas jest najczęściej bezpośrednie - nie jest ono inne dla funkcji MessageReceived() aniżeli dla jakiejś innej funkcji. Ale co zdarzyło się na szczycie hierarchii - w samej klasie BHandler - dodano małą zmarszczenie. Implementacja funkcji MessageReceived() BHandler'a szuka następnego handler'a w łańcuchu handler'a BLooper'a i wywołuje funkcję MessageReceived() tego obiektu.


Wysyłanie komunikatu

Są dwie funkcje, które wysyłają komunikaty do różnych odbiorców:

Funkcja PostMessage()

Możesz wysłać komunikat jeśli odbiorca BLooper'a jest w Twojej aplikacji:

myLooper->PostMessage(new BMessage(DO_SOMETHING), targetHandler);

Jak pokazano tutaj, możesz określić handler który chcesz mieć do obsługiwania posyłanego komunikatu. Jedynym wymaganiem jest to, że BHandler musi należeć do BLooper'a.

Jeśli argument handler'a jest równy NULL, komunikat jest obsługiwany przez looper'a. preferowany handler

myLooper->PostMessage(new BMessage(DO_SOMETHING), NULL);

Przez użycie domyślnego handler'a, pozwalasz looper'owi rozstrzygać o tym, kto powinien obsłużyć komunikat.

Twórca komunikatu BMessage zachowuje prawo własności i jest odpowiedzialny za usunięcie go jeśli nie jest on już dłużej potrzebny.

Funkcja SendMessage()

Jeśli chcesz wysłąć komunikat do kolejnej aplikacji, możesz użyć funkcji SendMessage() BMessenger'a. Najpierw konstruujesz obiekt BMessenger, który identyfikuje zdalną aplikację przez sygnaturę...

BMessenger messenger("application/x-some-app");

...a następnie wywołujesz SendMessage():

messenger.SendMessage(new BMessage(DO_SOMETHING));

Twórca BMessage'a zachowuje prawo własności i jest odpowiedzialny za jego usunięcie gdy nie jest on już dłużej potrzebny.

Obsługując odpowiedź

Każdy BMessage , który wysyłasz identyfukuje aplikację z której był on wysłany. Odbiorca komunikatu może odpowiedzieć na komunikat Odbiorca komunikatu może odpowiedzieć na komunikat, czy Ty ( nadawca) oczekujesz na odpowiedź czy nie. Domyślnie, komunikaty odpowiedzi są obsługiwane przez Twój obiekt BApplication. Jeśli chcesz aby komunikaty odpowiedzi były obsługiwane przez jakiś inny BHandler, określ obiekt jako końcowy argument wywołania PostMesssage() lub SendMessage():

myLooper->PostMessage(new BMessage(DO_SOMETHING), targetHandler, replyHandler);
/* i */
myMessenger.SendMessage(&message, replyHandler);

Odpowiedź jest wysyłana asynchronicznie w odniesieniu do funkcji PostMessage()/SendMessage().

SendMessage() (tylko) pozwala Ci zapytać się o komunikat odpowiedzi, który jest przekazywany spowrotem synchronicznie w swoim wywołaniu SendMessage():

BMessage reply;
myMessenger.SendMessage(&message, &reply);

SendMessage() nie zwraca wartości dopóki nie otrzyma odpowiedzi. Jeśli odbiorca nie odpowie wystarczająco szybko, jest tworzony i zwracany komunikat domyślny.

Odbieranie komunikatu

Funkcja SendReply() BMessage'a ma taką sama składnię jak SendMessage(), więc jest możliwe pytanie o synchroniczną odpowiedź na komunikat, który sam jest odpowiedzią,

BMessage message(READY);
BMessage reply;
theMessage->SendReply(&message, &reply);
if ( reply->what != B_NO_REPLY ) {
   . . .
}

lub wyznaczenie BHandler'a dla odpowiedzi na odpowiedź:

theMessage->SendReply(&message, someHandler);

W ten sposób, dwie aplikacje mogą utrzymywać toczącą się wymianę komunikatów.

Przypisanie handler'a

Aby zostac powiadomionym o nadchodzącym komunikacie, BHandler musi "należeć" do BLooper; a ten musi zostać dodany do listy odpowiednich handler'ów BLooper'a. Lista może zawierać dowolną liczbę obiektów, ale w danej chwili BHandler może należeć tylko do jednego BLooper'a.

Handler'y, które należą do tego samego BLooper'a mogą być powiązane w listę łączoną (ang. linked list). Jeśli obiekt nie może odpowiedzieć na komunikat, system przekazuje komunikat do następnego handler'a.

Funkccja AddHandler() BLooper'a ustawia przypisanie looper-handler; SetNextHandler() BHandler'a ustawia połączenie handler-handler.


Filtry komunikatu

Klasa BMessageFilter pozwala tworzyć funkcje filtrujące, które sprawdzają i przekierowują (lub odrzucają) nadchodzące komunikaty przed ich przetworzeniem przez BLooper. Filtry komunikatów mogą być również zastosowane do pojedynczych obiektów typu BHandler.


Protokoły komunikatu

Zarówno źródło jak i adresat komunikatu muszą zgodzić się na jego format - stała polecenia, nazwy i typy pól danych. Muszą oni również uzgodnić szczegóły wymiany - kiedy komunikat może zostać wysłany, czy wymaga on odpowiedzi, jaki powinien być format odpowiedzi, co to znaczy, jeśli oczekiwany element danych jest pominięty i tak dalej.

Nie jest to żaden problem dla komunikatów, które są używane tylko wewnątrz aplikacji; projektant aplikacji może utrzymywać ślady szczegółów developer can keep track of the details. Jednak, protokoły muszą być opublikowane dla komunikatów, których używają do komunikowania się aplikacje. Jesteś nakłaniany do opublikowania specyfikacji dla wszystkich komunikatów, które twoja aplikacja jest skłonna przyjąć z zewnętrznych źródeł i dla tych wszystkich, które może ona zapakować aby doręczyć je do innych aplikacji.


Be Book Application Kit Application Kit Indeks

Be Book,
...w ślicznym HTML...
dla BeOS wydanie 5

Copyright © 2000 Be, Inc. Wszelkie prawa zastrzeżone.