Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00224 001941 12922916 na godz. na dobę w sumie
Android. Aplikacje wielowątkowe. Techniki przetwarzania - książka
Android. Aplikacje wielowątkowe. Techniki przetwarzania - książka
Autor: Liczba stron: 248
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-9614-7 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> java - programowanie
Porównaj ceny (książka, ebook (-25%), audiobook).

Wykorzystaj w pełni potencjał wątków!

Jeżeli chcesz tworzyć aplikacje dostarczające użytkownikom doskonałych wrażeń, jeżeli chcesz wykonywać skomplikowane zadania szybciej lub po prostu musisz jednocześnie wykonywać różne działania — naucz się korzystać z wątków. Tworzenie programów wielowątkowych jest trudne, ale opanowanie tej sztuki pozwoli Ci osiągnąć wymierne korzyści!

Ta książka w całości poświęcona jest korzystaniu z wątków na platformie Android. Dzięki niej poznasz przeróżne sposoby asynchronicznego przetwarzania oraz ich zalety i wady. Jednak na samym początku zapoznasz się z podstawowymi informacjami dotyczącymi wielowątkowości w języku Java. Dowiesz się, w jaki sposób wątki komunikują się ze sobą i synchronizują dostęp do zasobów oraz jak nimi zarządzać. Kolejne rozdziały zawierają sporą dawkę wiedzy na temat różnych technik asynchronicznych. Zapoznanie się z ich treścią ułatwi Ci wybór techniki, która spełni Twoje wymagania, gdy sam zabierzesz się za tworzenie aplikacji wielowątkowej. Książka ta jest obowiązkową lekturą dla programistów chcących w pełni wykorzystać możliwości platformy Android.

Przewodnik po świecie wątków platformy Android!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

• Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:316)ci Przedmowa ...................................................................................................................11 1. Komponenty systemu Android Aplikacja Komponenty oraz potrzeba przetwarzania wieloprocesorowego ................................................. 15 Stos programowy systemu Android 15 16 Architektura aplikacji 17 17 19 19 20 23 23 25 Tworzenie aplikacji responsywnych za pomoc(cid:241) w(cid:241)tków Strukturyzacja aplikacji w celu zwi(cid:246)kszenia wydajno(cid:264)ci Procesy systemu Linux Cykl (cid:276)ycia Wykonywanie aplikacji Podsumowanie Cz(cid:253)(cid:316)(cid:235) I. Podstawy ............................................................................................27 Bezpiecze(cid:254)stwo w(cid:241)tków Podstawy w(cid:241)tków Wykonywanie Aplikacja jednow(cid:241)tkowa Aplikacja wielow(cid:241)tkowa 2. Wielow(cid:233)tkowo(cid:316)(cid:235) w Javie ............................................................................................29 29 29 30 31 33 34 35 37 38 39 40 Blokada wewn(cid:246)trzna i monitor Javy Synchronizowanie dost(cid:246)pu do zasobów wspó(cid:228)dzielonych Przyk(cid:228)ad: konsument i producent Model wykonywania wspó(cid:228)bie(cid:276)nego Strategie wykonywania zada(cid:254) Podsumowanie 5 Kup książkęPoleć książkę W(cid:241)tki aplikacji w systemie Android W(cid:241)tki interfejsu u(cid:276)ytkownika W(cid:241)tki wi(cid:241)zania W(cid:241)tki w tle 3. W(cid:233)tki w systemie Android .......................................................................................... 41 41 41 42 42 43 45 48 Proces i w(cid:241)tki systemu Linux Podsumowanie Szeregowanie Potoki Sygnalizacja Pami(cid:246)(cid:232) wspó(cid:228)dzielona Interfejs BlockingQueue Przesy(cid:228)anie komunikatów w systemie Android Podstawowe zastosowanie potoków Przyk(cid:228)ad: przetwarzanie tekstu w w(cid:241)tku roboczym 4. Komunikacja w(cid:233)tków ..................................................................................................49 49 50 51 53 54 55 56 57 59 63 66 67 75 76 79 80 Przyk(cid:228)ad: podstawowe przesy(cid:228)anie komunikatów Klasy stosowane w przesy(cid:228)aniu komunikatów Komunikaty Looper Handler Usuwanie komunikatów z kolejki Obserwowanie kolejki komunikatów Komunikacja z w(cid:241)tkiem interfejsu u(cid:276)ytkownika Podsumowanie Binder J(cid:246)zyk AIDL RPC systemu Android 5. Komunikacja mi(cid:253)dzy procesami ................................................................................. 81 81 82 83 84 86 88 89 91 92 Synchroniczne wywo(cid:228)anie RPC Asynchroniczne wywo(cid:228)anie RPC Komunikacja jednokierunkowa Komunikacja dwukierunkowa Przekazywanie komunikatów za pomoc(cid:241) obiektu Binder Podsumowanie Odzyskiwanie pami(cid:246)ci Wycieki pami(cid:246)ci zwi(cid:241)zane z w(cid:241)tkiem 6. Zarz(cid:233)dzanie pami(cid:253)ci(cid:233) .................................................................................................93 93 95 96 101 Wykonywanie w(cid:241)tku Komunikacja w(cid:241)tków 6 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę Unikanie wycieków pami(cid:246)ci Korzystanie ze statycznych klas wewn(cid:246)trznych Korzystanie ze s(cid:228)abych referencji Zatrzymywanie wykonywania w(cid:241)tku roboczego Zachowanie w(cid:241)tków roboczych Czyszczenie kolejki komunikatów Podsumowanie 103 104 104 105 105 105 106 Cz(cid:253)(cid:316)(cid:235) II. Techniki asynchroniczne .................................................................107 7. Zarz(cid:233)dzanie cyklem (cid:348)ycia w(cid:233)tku podstawowego ................................................... 109 109 109 110 112 113 113 115 120 Cykl (cid:276)ycia Przerwania Wyj(cid:241)tki nieprzechwycone Definiowanie i uruchamianie Retencja Zarz(cid:241)dzanie w(cid:241)tkami Podstawy Podsumowanie Podstawy Cykl (cid:276)ycia Przypadki u(cid:276)ycia 8. Klasa HandlerThread: wysokopoziomowy mechanizm kolejkowania ....................121 121 123 124 124 125 127 129 130 Powtarzaj(cid:241)ce si(cid:246) wykonywanie zadania Zadania powi(cid:241)zane (cid:227)a(cid:254)cuchowanie zada(cid:254) Warunkowe wstawianie zadania Podsumowanie Interfejs Executor Pule w(cid:241)tków Predefiniowane pule w(cid:241)tków Niestandardowe pule w(cid:241)tków Projektowanie puli w(cid:241)tków Cykl (cid:276)ycia Zamykanie puli w(cid:241)tków Przypadki u(cid:276)ycia i pu(cid:228)apki pul w(cid:241)tków 9. Kontrola wykonywania w(cid:233)tku za pomoc(cid:233) frameworku wykonawcy .....................131 131 133 134 135 136 139 140 141 143 143 144 147 Reprezentacja zadania Zatwierdzanie zada(cid:254) Odrzucanie zada(cid:254) Zarz(cid:241)dzanie zadaniami Spis tre(cid:316)ci (cid:95) 7 Kup książkęPoleć książkę Klasa ExecutorCompletionService Podsumowanie 148 150 10. Wi(cid:233)zanie zadania w tle z w(cid:233)tkiem interfejsu Implementacja klasy AsyncTask Przyk(cid:228)ad: pobieranie obrazów Wykonywanie zadania w tle Tworzenie i uruchamianie Anulowanie Stany u(cid:348)ytkownika za pomoc(cid:233) klasy AsyncTask .................................................................151 Podstawy 151 154 154 155 156 157 160 161 162 164 165 165 166 166 167 167 Wykonywanie globalne dla aplikacji Wykonywanie zada(cid:254) w ró(cid:276)nych wersjach platformy Wykonywanie niestandardowe Alternatywy dla klasy AsyncTask Trywialne implementacje klasy AsyncTask Zadania w tle wymagaj(cid:241)ce instancji Looper Us(cid:228)uga lokalna Korzystanie z metody execute(Runnable) Podsumowanie 11. Us(cid:293)ugi ......................................................................................................................... 169 Dlaczego warto wykorzysta(cid:232) komponent Service do wykonywania asynchronicznego? Us(cid:228)ugi lokalne, zdalne i globalne Tworzenie i wykonywanie Cykl (cid:276)ycia Us(cid:228)uga uruchamiana Implementacja metody onStartCommand Opcje ponownego uruchamiania Us(cid:228)uga kontrolowana przez u(cid:276)ytkownika Us(cid:228)uga kontrolowana przez zadanie Us(cid:228)uga wi(cid:241)zana Wi(cid:241)zanie lokalne Wybór techniki asynchronicznej Podsumowanie 169 171 172 173 174 175 176 178 181 183 184 187 188 Podstawy Dobre sposoby wykorzystania klasy IntentService 12. Klasa IntentService .................................................................................................... 189 189 190 191 193 Zadania uporz(cid:241)dkowane sekwencyjnie Wykonywanie asynchroniczne w komponencie BroadcastReceiver 8 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę Porównanie klas IntentService oraz Service Podsumowanie 196 196 13. Uzyskiwanie dost(cid:253)pu do klasy ContentProvider za pomoc(cid:233) klasy AsyncQueryHandler ...................................................................... 197 Krótkie wprowadzenie do klasy ContentProvider 197 199 Uzasadnienie dla przetwarzania w tle klasy ContentProvider 200 Korzystanie z klasy AsyncQueryHandler Przyk(cid:228)ad: rozszerzanie listy kontaktów 201 204 Klasa AsyncQueryHandler 205 Ograniczenia Podsumowanie 205 Bezproblemowe (cid:228)adowanie danych za pomoc(cid:241) (cid:228)adowarki CursorLoader Implementowanie niestandardowych (cid:228)adowarek Framework (cid:228)adowarek Klasa LoaderManager Interfejsy LoaderCallbacks Klasa AsyncTaskLoader Korzystanie z (cid:228)adowarki CursorLoader Przyk(cid:228)ad: lista kontaktów Dodawanie obs(cid:228)ugi operacji CRUD 14. Automatyczne wykonywanie w tle za pomoc(cid:233) (cid:293)adowarek ....................................207 208 209 211 213 213 214 214 215 219 219 220 222 223 223 226 227 Cykl (cid:276)ycia (cid:228)adowarki (cid:227)adowanie w tle Zarz(cid:241)dzanie tre(cid:264)ci(cid:241) Dostarczanie zbuforowanych rezultatów Przyk(cid:228)ad: niestandardowa (cid:228)adowarka plików Obs(cid:228)uga wielu (cid:228)adowarek Podsumowanie 15. Podsumowanie: wybór techniki asynchronicznej ....................................................229 230 230 231 231 232 Zachowanie prostoty Zarz(cid:241)dzanie zasobami i w(cid:241)tkami Wymiana komunikatów w celu uzyskania responsywno(cid:264)ci Unikanie nieoczekiwanego zako(cid:254)czenia zadania (cid:227)atwy dost(cid:246)p do klasy ContentProvider A Bibliografia ................................................................................................................235 Skorowidz ..................................................................................................................237 Spis tre(cid:316)ci (cid:95) 9 Kup książkęPoleć książkę 10 (cid:95) Spis tre(cid:316)ci Kup książkęPoleć książkę ROZDZIA(cid:292) 4. Komunikacja w(cid:233)tków W aplikacjach wielow(cid:241)tkowych zadania mog(cid:241) dzia(cid:228)a(cid:232) równolegle i wspó(cid:228)pracowa(cid:232) w celu uzy- skania rezultatów. Dlatego w(cid:241)tki musz(cid:241) by(cid:232) w stanie komunikowa(cid:232) si(cid:246), aby umo(cid:276)liwi(cid:232) praw- dziwe asynchroniczne przetwarzanie. W systemie Android znaczenie komunikacji w(cid:241)tków zosta(cid:228)o zaakcentowane w charakterystycznym dla platformy mechanizmie Handler/Looper (proce- dura obs(cid:228)ugi/procedura zap(cid:246)tlania). Na tym mechanizmie oraz tradycyjnych technikach Javy skupimy si(cid:246) w tym rozdziale. Rozdzia(cid:228) obejmuje: (cid:120) przekazywanie danych przez jednokierunkowe potoki danych; (cid:120) komunikacj(cid:246) za pomoc(cid:241) pami(cid:246)ci wspó(cid:228)dzielonej; (cid:120) implementacj(cid:246) wzorca konsument – producent za pomoc(cid:241) interfejsu BlockingQueue; (cid:120) operacje na kolejkach komunikatów; (cid:120) wysy(cid:228)anie zadania z powrotem do w(cid:241)tku interfejsu u(cid:276)ytkownika. Potoki Potoki s(cid:241) cz(cid:246)(cid:264)ci(cid:241) paczki java.io. Oznacza to, (cid:276)e s(cid:241) one ogóln(cid:241) funkcjonalno(cid:264)ci(cid:241) Javy, a nie s(cid:241) charakterystyczne dla platformy Android. Dla dwóch w(cid:241)tków w ramach tego samego procesu potok stanowi sposób po(cid:228)(cid:241)czenia i ustanowienia jednokierunkowego kana(cid:228)u danych. W(cid:241)tek producenta zapisuje dane do potoku, natomiast w(cid:241)tek konsumenta odczytuje dane z potoku. Potok Java jest porównywalny do operatora potoku systemów Unix i Linux (znak specjalny | pow(cid:228)oki), który jest u(cid:276)ywany do przekierowania wyj(cid:264)cia z jednego pole- cenia na wej(cid:264)cie do innego polecenia. Operator potoku dzia(cid:228)a w systemie Linux przez granice procesów, ale potoki Java dzia(cid:228)aj(cid:241) pomi(cid:246)dzy w(cid:241)tkami w maszynie wirtualnej, np. w ramach procesu. Sam potok jest buforem alokowanym w pami(cid:246)ci, dost(cid:246)pnym tylko dla dwóch po(cid:228)(cid:241)czonych w(cid:241)tków. (cid:275)adne inne w(cid:241)tki nie mog(cid:241) uzyska(cid:232) dost(cid:246)pu do danych. Dlatego zapewnione jest bezpiecze(cid:254)stwo w(cid:241)tków, które zosta(cid:228)o omówione w rozdziale 2., w podrozdziale „Bezpiecze(cid:254)- stwo w(cid:241)tków”. Potok jest tak(cid:276)e jednokierunkowy, co pozwala tylko jednemu w(cid:241)tkowi zapisy- wa(cid:232) dane, a drugiemu odczytywa(cid:232) (patrz rysunek 4.1). 49 Kup książkęPoleć książkę Rysunek 4.1. Komunikacja w(cid:241)tków z wykorzystaniem potoków Potoki s(cid:241) zazwyczaj u(cid:276)ywane, gdy masz dwa d(cid:228)ugo wykonywane zadania i musisz w sposób ci(cid:241)g(cid:228)y odci(cid:241)(cid:276)a(cid:232) jedno, przerzucaj(cid:241)c dane do drugiego. Potoki u(cid:228)atwiaj(cid:241) oddziela(cid:232) zadania do kilku w(cid:241)tków, zamiast obs(cid:228)ugiwa(cid:232) wiele zada(cid:254) za pomoc(cid:241) tylko jednego w(cid:241)tku. Gdy jedno za- danie wyprodukuje wynik w w(cid:241)tku, wynik ten jest potokowany do nast(cid:246)pnego w(cid:241)tku, który dalej przetwarza dane. Zysk pochodzi z czystej separacji kodu i wspó(cid:228)bie(cid:276)nego wykonywania. Potoki mog(cid:241) by(cid:232) stosowane mi(cid:246)dzy w(cid:241)tkami roboczymi i do odci(cid:241)(cid:276)ania pracy w(cid:241)tku interfejsu u(cid:276)ytkownika, który nale(cid:276)y oszcz(cid:246)dza(cid:232), aby zachowa(cid:232) responsywno(cid:264)(cid:232) w do(cid:264)wiadczeniu u(cid:276)yt- kownika. Potok mo(cid:276)e przesy(cid:228)a(cid:232) dane binarne lub znakowe. Transfer danych binarnych jest reprezen- towany przez klasy PipedOutputStream (u producenta) i PipedInputStream (u konsumenta). Natomiast transfer danych znakowych jest reprezentowany przez klasy PipedWriter (u pro- ducenta) i PipedReader (u konsumenta). Niezale(cid:276)nie od typu transferu danych oba te potoki maj(cid:241) podobn(cid:241) funkcjonalno(cid:264)(cid:232). (cid:275)ycie potoku zaczyna si(cid:246), gdy w(cid:241)tek zapisuj(cid:241)cy lub odczytuj(cid:241)cy ustanawia po(cid:228)(cid:241)czenie, a ko(cid:254)czy, gdy po(cid:228)(cid:241)czenie jest zamykane. Podstawowe zastosowanie potoków Podstawowy cykl (cid:276)ycia potoku mo(cid:276)na podsumowa(cid:232) w trzech etapach: konfiguracja, transfer danych (który mo(cid:276)e by(cid:232) powtarzany tak d(cid:228)ugo, jak te dwa w(cid:241)tki chc(cid:241) wymienia(cid:232) dane) oraz od(cid:228)(cid:241)czenie. Poni(cid:276)sze przyk(cid:228)ady zosta(cid:228)y przygotowane z wykorzystaniem klas PipedWriter i PipedReader, ale te same etapy dotycz(cid:241) klas PipedOutputStream i PipedInputStream. 1. Konfigurowanie po(cid:228)(cid:241)czenia: PipedReader r = new PipedReader(); PipedWriter w = new PipedWriter(); w.connect(r); W tym przypadku po(cid:228)(cid:241)czenie jest nawi(cid:241)zywane przez w(cid:241)tek zapisuj(cid:241)cy (cid:228)(cid:241)cz(cid:241)cy si(cid:246) z w(cid:241)tkiem odczytuj(cid:241)cym. Po(cid:228)(cid:241)czenie mo(cid:276)e równie dobrze zosta(cid:232) ustanowione przez w(cid:241)tek odczytuj(cid:241)cy. Potok domy(cid:264)lnie konfiguruje równie(cid:276) kilka konstruktorów. Domy(cid:264)lny rozmiar bufora to 1024, ale mo(cid:276)na go konfigurowa(cid:232) od strony konsumenta potoku, tak jak poka- zano poni(cid:276)ej: int BUFFER_SIZE_IN_CHARS = 1024 * 4; PipedReader r = new PipedReader(BUFFER_SIZE_IN_CHARS); PipedWriter w = new PipedWriter(r); 2. Przekazanie czytnika do w(cid:241)tku przetwarzania: Thread t = new MyReaderThread(r); t.start(); Po uruchomieniu w(cid:241)tek odczytuj(cid:241)cy jest gotowy do odbioru danych z w(cid:241)tku zapisuj(cid:241)cego. 50 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę 3. Transfer danych: // W(cid:261)tek producenta: zapisuje pojedynczy znak lub tablic(cid:266) znaków. w.write( A ); // W(cid:261)tek konsumenta: odczytuje dane. int result = r.read(); Komunikacja stosuje si(cid:246) do wzorca konsument – producent z mechanizmem blokowania. Je(cid:264)li potok jest pe(cid:228)ny, metoda write() b(cid:246)dzie blokowa(cid:232), a(cid:276) odpowiednia ilo(cid:264)(cid:232) danych zostanie odczytana, a w konsekwencji usuni(cid:246)ta z potoku, aby zwolni(cid:232) miejsce dla danych, które próbuje doda(cid:232) w(cid:241)tek zapisuj(cid:241)cy. Metoda read() blokuje za ka(cid:276)dym razem, gdy nie ma danych do odczytu z potoku. Warto zauwa(cid:276)y(cid:232), (cid:276)e metoda read() zwraca znak jako warto(cid:264)(cid:232) ca(cid:228)kowit(cid:241), aby upewni(cid:232) si(cid:246), (cid:276)e dost(cid:246)pna jest wystarczaj(cid:241)ca ilo(cid:264)(cid:232) miejsca do obs(cid:228)ugi ró(cid:276)nego kodowania o ró(cid:276)nych rozmiarach. Mo(cid:276)esz zrzuci(cid:232) warto(cid:264)(cid:232) ca(cid:228)kowit(cid:241) z powrotem do znaku. W praktyce najlepsze podej(cid:264)cie b(cid:246)dzie wygl(cid:241)da(cid:232) nast(cid:246)puj(cid:241)co: // W(cid:261)tek producenta: sp(cid:225)ukuje potok po wykonaniu zapisu. w.write( A ); w.flush(); // W(cid:261)tek konsumenta: odczytuje dane w p(cid:266)tli. int i; while((i = reader.read()) != -1){ char c = (char) i; // Obs(cid:225)uguje odebrane dane. } Wywo(cid:228)anie metody flush() po zapisie do potoku informuje w(cid:241)tek konsumenta, (cid:276)e do- st(cid:246)pne s(cid:241) nowe dane. Jest to przydatne z punktu widzenia wydajno(cid:264)ci, poniewa(cid:276) gdy bu- for jest pusty, PipedReader u(cid:276)ywa wywo(cid:228)ania blokuj(cid:241)cego do metody wait() z jednose- kundowym limitem czasu. Dlatego je(cid:264)li wywo(cid:228)anie metody flush() zostanie pomini(cid:246)te, w(cid:241)tek konsumenta mo(cid:276)e opó(cid:274)ni(cid:232) odczyt danych o maksymalnie jedn(cid:241) sekund(cid:246). Wywo- (cid:228)uj(cid:241)c metod(cid:246) flush(), producent skraca czas oczekiwania w w(cid:241)tku konsumenta i umo(cid:276)li- wia natychmiastowe kontynuowanie przetwarzania danych. 4. Zamkni(cid:246)cie po(cid:228)(cid:241)czenia. Po zako(cid:254)czeniu fazy komunikacji potok powinien zosta(cid:232) od(cid:228)(cid:241)czony: // W(cid:261)tek producenta: zamykanie modu(cid:225)u zapisu. w.close(); // W(cid:261)tek konsumenta: zamykanie modu(cid:225)u odczytu. r.close(); Je(cid:264)li modu(cid:228)y zapisuj(cid:241)cy i odczytuj(cid:241)cy s(cid:241) po(cid:228)(cid:241)czone, wystarczy tylko zamkn(cid:241)(cid:232) jeden z nich. Je(cid:264)li modu(cid:228) zapisuj(cid:241)cy zostanie zamkni(cid:246)ty, potok jest od(cid:228)(cid:241)czany, ale dane znajduj(cid:241)ce si(cid:246) w buforze wci(cid:241)(cid:276) mo(cid:276)na odczyta(cid:232). Je(cid:264)li zamkni(cid:246)ty zostanie modu(cid:228) odczytuj(cid:241)cy, bufor jest czyszczony. Przyk(cid:293)ad: przetwarzanie tekstu w w(cid:233)tku roboczym Nast(cid:246)pny przyk(cid:228)ad ilustruje sposób, w jaki potoki mog(cid:241) przetwarza(cid:232) tekst, który u(cid:276)ytkownik wprowadza w kontrolce EditText. Aby zachowa(cid:232) responsywno(cid:264)(cid:232) w(cid:241)tku interfejsu u(cid:276)yt- kownika, ka(cid:276)dy znak wprowadzony przez u(cid:276)ytkownika jest przekazywany do w(cid:241)tku robo- czego, który przypuszczalnie obs(cid:228)uguje pewne czasoch(cid:228)onne przetwarzanie: Potoki (cid:95) 51 Kup książkęPoleć książkę public class PipeExampleActivity extends Activity { private static final String TAG = PipeExampleActivity ; private EditText editText; PipedReader r; PipedWriter w; private Thread workerThread; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); r = new PipedReader(); w = new PipedWriter(); try { w.connect(r); } catch (IOException e) { e.printStackTrace(); } setContentView(R.layout.activity_pipe); editText = (EditText) findViewById(R.id.edit_text); editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) { } @Override public void onTextChanged(CharSequence charSequence, int start, int before, int count) { try { // Obs(cid:225)uguje tylko dodawanie znaków. if(count before) { // Zapisuje do potoku ostatni wprowadzony znak. w.write(charSequence.subSequence(before, count). toString()); } } catch (IOException e) { e.printStackTrace(); } } @Override public void afterTextChanged(Editable editable) { } }); workerThread = new Thread(new TextHandlerTask(r)); workerThread.start(); } @Override protected void onDestroy() { super.onDestroy(); workerThread.interrupt(); try { r.close(); w.close(); } catch (IOException e) { } } 52 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę private static class TextHandlerTask implements Runnable { private final PipedReader reader; public TextHandlerTask(PipedReader reader){ this.reader = reader; } @Override public void run() { while(Thread.currentThread().isInterrupted()){ try { int i; while((i = reader.read()) != -1){ char c = (char) i; //TUTAJ DODAJ LOGIK(cid:265) PRZETWARZANIA TEKSTU. Log.d(TAG, char = + c); } } catch (IOException e) { e.printStackTrace(); } } } } } Gdy zostanie utworzona aktywno(cid:264)(cid:232) PipeExampleActivity, wy(cid:264)wietlane jest pole EditText, które posiada nas(cid:228)uchiwacz (TextWatcher) zmian w zawarto(cid:264)ci. Za ka(cid:276)dym razem, gdy w polu EditText dodawany jest nowy znak, jest on zapisywany do potoku i odczytywany w klasie TextHandlerTask. Zadaniem konsumenta jest wykonywanie niesko(cid:254)czonej p(cid:246)tli, która od- czytuje znak z potoku tak d(cid:228)ugo, jak jest co(cid:264) do odczytania. P(cid:246)tla wewn(cid:246)trzna while b(cid:246)dzie blokowa(cid:232) przy wywo(cid:228)aniu metody read(), je(cid:264)li potok jest pusty. B(cid:241)d(cid:274) ostro(cid:276)ny, stosuj(cid:241)c potoki z w(cid:241)tkiem interfejsu u(cid:276)ytkownika, ze wzgl(cid:246)du na mo(cid:276)- liwo(cid:264)(cid:232) blokowania wywo(cid:228)a(cid:254), je(cid:264)li potok jest pe(cid:228)ny (producent blokuje na wywo(cid:228)aniu swojej metody write()) lub pusty (konsument blokuje na wywo(cid:228)aniu swojej me- tody read()). Pami(cid:253)(cid:235) wspó(cid:293)dzielona Pami(cid:246)(cid:232) wspó(cid:228)dzielona (u(cid:276)ywaj(cid:241)ca obszaru pami(cid:246)ci znanego w programowaniu jako sterta) jest popularnym sposobem przekazywania informacji pomi(cid:246)dzy w(cid:241)tkami. Wszystkie w(cid:241)tki w aplikacji mog(cid:241) uzyska(cid:232) dost(cid:246)p do tej samej przestrzeni adresowej w procesie. Dlatego je(cid:264)li jeden w(cid:241)tek zapisuje warto(cid:264)(cid:232) w zmiennej w pami(cid:246)ci wspólnej, warto(cid:264)(cid:232) ta mo(cid:276)e by(cid:232) odczytana przez wszystkie inne w(cid:241)tki, tak jak pokazano na rysunku 4.2. Je(cid:264)li w(cid:241)tek przechowuje dane jako zmienn(cid:241) lokaln(cid:241), (cid:276)aden inny w(cid:241)tek nie mo(cid:276)e ich zoba- czy(cid:232). Przechowuj(cid:241)c dane w pami(cid:246)ci wspó(cid:228)dzielonej, w(cid:241)tek mo(cid:276)e u(cid:276)ywa(cid:232) zmiennych do ko- munikacji z innymi w(cid:241)tkami i dzielenia si(cid:246) z nimi prac(cid:241). Obiekty s(cid:241) przechowywane w pa- mi(cid:246)ci wspó(cid:228)dzielonej, je(cid:264)li znajduj(cid:241) si(cid:246) w jednym z nast(cid:246)puj(cid:241)cych zakresów: (cid:120) zmienne cz(cid:228)onków instancji, (cid:120) zmienne cz(cid:228)onków klasy, (cid:120) obiekty zadeklarowane w metodach. Pami(cid:253)(cid:235) wspó(cid:293)dzielona (cid:95) 53 Kup książkęPoleć książkę Rysunek 4.2. Komunikacja w(cid:241)tków za pomoc(cid:241) pami(cid:246)ci wspó(cid:228)dzielonej Referencja obiektu jest przechowywana lokalnie w stosie w(cid:241)tku, ale sam obiekt jest przecho- wywany w pami(cid:246)ci wspó(cid:228)dzielonej. Obiekt jest dost(cid:246)pny z wielu w(cid:241)tków, je(cid:264)li tylko metoda publikuje referencj(cid:246) poza zakresem metody, np. przez przekazanie tej referencji do metody innego obiektu. W(cid:241)tki komunikuj(cid:241) si(cid:246) poprzez pami(cid:246)(cid:232) wspó(cid:228)dzielon(cid:241), definiuj(cid:241)c pola instancji i klasy, które s(cid:241) dost(cid:246)pne z wielu w(cid:241)tków. Sygnalizacja Podczas gdy w(cid:241)tki komunikuj(cid:241) si(cid:246) poprzez zmienne stanu w pami(cid:246)ci wspó(cid:228)dzielonej, mog(cid:241) odpytywa(cid:232) warto(cid:264)(cid:232) stanu, aby pobiera(cid:232) jego zmiany. Bardziej wydajny jest jednak wbudo- wany w bibliotece j(cid:246)zyka Java mechanizm sygnalizacji, który pozwala w(cid:241)tkowo powiadomi(cid:232) inne w(cid:241)tki o zmianach swojego stanu. Mechanizm sygnalizacji ró(cid:276)ni si(cid:246) w zale(cid:276)no(cid:264)ci od typu synchronizacji (patrz tabela 4.1). Tabela 4.1. Sygnalizacja w(cid:241)tków synchronized ReentrantLock ReentrantReadWriteLock Wywo(cid:293)anie blokuj(cid:233)ce, oczekiwanie na stan Sygna(cid:293) dla zablokowanych w(cid:233)tków Object.wait() Object.wait(timeout) Object.notify() Object.notifyAll() Condition.await() Condition.await(timeout) Condition.signal() Condition.signalAll() Condition.await() Condition.await(timeout) Condition.signal() Condition.signalAll() Gdy w(cid:241)tek nie mo(cid:276)e kontynuowa(cid:232) wykonywania, dopóki inny w(cid:241)tek nie osi(cid:241)gnie okre(cid:264)lonego stanu, wywo(cid:228)uje metod(cid:246) wait()/wait(timeout) lub jej odpowiednik await()/await(timeout), w zale(cid:276)no(cid:264)ci od zastosowanej synchronizacji. Parametry limitu czasu timeout wskazuj(cid:241), jak d(cid:228)ugo w(cid:241)tek wywo(cid:228)uj(cid:241)cy powinien czeka(cid:232) przed kontynuowaniem wykonywania. Gdy inny w(cid:241)tek zmienia swój stan, sygnalizuje t(cid:246) zmian(cid:246) wywo(cid:228)aniem metody notify()/ notifyAll() lub jej odpowiednika signal()/signalAll(). Po sygnale w(cid:241)tek oczekuj(cid:241)cy kontynuuje wykonywanie. W ten sposób wywo(cid:228)ania obs(cid:228)uguj(cid:241) dwa ró(cid:276)ne wzorce projektowe, które korzystaj(cid:241) z warunków: wersja notify() lub signal() budzi jeden wybrany losowo w(cid:241)tek, natomiast wersja notifyAll() lub signalAll() budzi wszystkie w(cid:241)tki oczekuj(cid:241)ce na sygna(cid:228). Poniewa(cid:276) sygna(cid:228) mo(cid:276)e by(cid:232) odbierany przez wiele w(cid:241)tków i jeden z nich mo(cid:276)e wej(cid:264)(cid:232) do sekcji krytycznej, zanim pozosta(cid:228)e si(cid:246) obudz(cid:241), odbieranie sygna(cid:228)u nie gwarantuje, (cid:276)e zostanie osi(cid:241)- gni(cid:246)ty prawid(cid:228)owy stan. W(cid:241)tek oczekuj(cid:241)cy powinien stosowa(cid:232) wzorzec projektowy, w którym 54 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę przed dalszym wykonywaniem sprawdza, czy (cid:276)(cid:241)dany warunek zosta(cid:228) spe(cid:228)niony. Je(cid:264)li np. wspó(cid:228)dzielony stan jest chroniony synchronizacj(cid:241) na blokadzie wewn(cid:246)trznej, sprawd(cid:274) stan przed wywo(cid:228)aniem metody wait(): synchronized(this) { while(isConditionFulfilled == false) { wait(); } // Kiedy wykonywanie osi(cid:261)ga ten punkt, stan jest poprawny. } Ten wzorzec sprawdza, czy predykat warunku zosta(cid:228) spe(cid:228)niony. Je(cid:264)li nie, w(cid:241)tek blokuje przez wywo(cid:228)anie metody wait(). Gdy inny w(cid:241)tek powiadamia monitor i w(cid:241)tek oczekuj(cid:241)cy budzi si(cid:246), sprawdza ponownie, czy warunek ten zosta(cid:228) spe(cid:228)niony, a je(cid:264)li nie, blokuje ponownie, czekaj(cid:241)c na nowy sygna(cid:228). Bardzo typowym przypadkiem u(cid:276)ycia w systemie Android jest tworzenie w(cid:241)tku ro- boczego z w(cid:241)tku interfejsu u(cid:276)ytkownika i pozwolenie w(cid:241)tkowi roboczemu na wy- produkowanie wyniku, który b(cid:246)dzie u(cid:276)ywany przez pewien element interfejsu u(cid:276)ytkownika. W ten sposób w(cid:241)tek interfejsu u(cid:276)ytkownika musi czeka(cid:232) na wynik. Jednak w(cid:241)tek interfejsu u(cid:276)ytkownika nie powinien czeka(cid:232) na sygna(cid:228) z w(cid:241)tku dzia- (cid:228)aj(cid:241)cego w tle, poniewa(cid:276) mo(cid:276)e go to blokowa(cid:232). Zamiast tego nale(cid:276)y u(cid:276)y(cid:232) omówio- nego w dalszej cz(cid:246)(cid:264)ci rozdzia(cid:228)u mechanizmu przekazywania komunikatów systemu Android. Interfejs BlockingQueue Sygnalizacja w(cid:241)tków jest niskopoziomowym, wysoce konfigurowalnym mechanizmem, który mo(cid:276)e zosta(cid:232) przystosowany do wielu przypadków u(cid:276)ycia, ale mo(cid:276)e by(cid:232) równie(cid:276) traktowany jako technika najbardziej podatna na b(cid:228)(cid:246)dy. Dlatego platforma Java buduje wysokopozio- mowe abstrakcje na bazie mechanizmu sygnalizacji w(cid:241)tków, aby rozwi(cid:241)za(cid:232) problem jedno- kierunkowego przekazywania arbitralnych obiektów mi(cid:246)dzy w(cid:241)tkami. Ta abstrakcja jest cz(cid:246)sto nazywana „rozwi(cid:241)zaniem problemu synchronizacji producent – konsument”. Na ten pro- blem sk(cid:228)adaj(cid:241) si(cid:246) przypadki u(cid:276)ycia, w których mog(cid:241) by(cid:232) w(cid:241)tki produkuj(cid:241)ce zawarto(cid:264)(cid:232) (w(cid:241)tki producenta) i w(cid:241)tki konsumuj(cid:241)ce zawarto(cid:264)(cid:232) (w(cid:241)tki konsumenta). Producenci przekazuj(cid:241) komunikaty do konsumentów w celu przetworzenia. Po(cid:264)rednikiem mi(cid:246)dzy w(cid:241)tkami jest ko- lejka z funkcj(cid:241) blokowania, tj. java.util.concurrent.BlockingQueue (patrz rysunek 4.3). Rysunek 4.3. Komunikacja w(cid:241)tków z wykorzystaniem interfejsu BlockingQueue Interfejs BlockingQueue (cid:95) 55 Kup książkęPoleć książkę Interfejs BlockingQueue pe(cid:228)ni rol(cid:246) koordynatora pomi(cid:246)dzy w(cid:241)tkami producenta i konsu- menta, zawijaj(cid:241)c razem implementacj(cid:246) listy i sygnalizacj(cid:246). Lista zawiera konfigurowaln(cid:241) liczb(cid:246) elementów, które w(cid:241)tki producenta wype(cid:228)niaj(cid:241) dowolnymi komunikatami danych. Po drugiej stronie w(cid:241)tki konsumenta wyodr(cid:246)bniaj(cid:241) komunikaty w kolejno(cid:264)ci, w jakiej zosta(cid:228)y umieszczo- ne w kolejce, a nast(cid:246)pnie przetwarzaj(cid:241) je. Koordynacja mi(cid:246)dzy producentami i konsumentami jest niezb(cid:246)dna, je(cid:264)li utrac(cid:241) synchronizacj(cid:246), np. je(cid:264)li producenci przekazuj(cid:241) wi(cid:246)cej komunika- tów, ni(cid:276) konsumenci s(cid:241) w stanie obs(cid:228)u(cid:276)y(cid:232). Dlatego interfejs BlockingQueue wykorzystuje warunki w(cid:241)tków, aby zapewni(cid:232), (cid:276)e producenci nie b(cid:246)d(cid:241) mogli kolejkowa(cid:232) nowych komuni- katów, je(cid:264)li lista BlockingQueue jest pe(cid:228)na, oraz (cid:276)e konsumenci b(cid:246)d(cid:241) wiedzieli, kiedy dost(cid:246)p- ne s(cid:241) komunikaty do pobrania. Synchronizacj(cid:246) mi(cid:246)dzy w(cid:241)tkami mo(cid:276)na osi(cid:241)gn(cid:241)(cid:232) za pomoc(cid:241) sygnalizacji w(cid:241)tków, tak jak opisano w rozdziale 2., w punkcie „Przyk(cid:228)ad: konsument i pro- ducent”. Jednak interfejs BlockingQueue zarówno blokuje w(cid:241)tki, jak i sygnalizuje wa(cid:276)ne zmiany stanu, czyli lista nie jest pe(cid:228)na oraz lista nie jest pusta. Wzorzec konsument – producent zaimplementowany za pomoc(cid:241) klasy LinkedBlockingQueue mo(cid:276)na (cid:228)atwo wdro(cid:276)y(cid:232) przez dodawanie komunikatów do kolejki metod(cid:241) put() i usuwanie ich metod(cid:241) take(). Metoda put() blokuje podmiot wywo(cid:228)uj(cid:241)cy, je(cid:264)li kolejka jest pe(cid:228)na, a metoda take() blokuje podmiot wywo(cid:228)uj(cid:241)cy, je(cid:264)li kolejka jest pusta: public class ConsumerProducer { private final int LIMIT = 10; private BlockingQueue Integer blockingQueue = new LinkedBlockingQueue Integer (LIMIT); public void produce() throws InterruptedException { int value = 0; while (true) { blockingQueue.put(value++); } } public void consume() throws InterruptedException { while (true) { int value = blockingQueue.take(); } } } Przesy(cid:293)anie komunikatów w systemie Android Dotychczas omawiane opcje komunikacji w(cid:241)tków s(cid:241) regularnymi opcjami j(cid:246)zyka Java, do- st(cid:246)pnymi w ka(cid:276)dej aplikacji Java. Te mechanizmy (potoki, pami(cid:246)(cid:232) wspó(cid:228)dzielona oraz kolejki blokuj(cid:241)ce) maj(cid:241) zastosowanie do aplikacji systemu Android, ale stwarzaj(cid:241) problemy w dzia(cid:228)aniu w(cid:241)tku interfejsu u(cid:276)ytkownika z powodu ich „sk(cid:228)onno(cid:264)ci” do blokowania. Responsywno(cid:264)(cid:232) w(cid:241)tku interfejsu u(cid:276)ytkownika jest zagro(cid:276)ona podczas korzystania z mechanizmów z funkcj(cid:241) blokowania, poniewa(cid:276) okazjonalnie mog(cid:241) one zawiesza(cid:232) w(cid:241)tek. Najbardziej powszechnym przypadkiem u(cid:276)ycia komunikacji w(cid:241)tków w systemie Android jest komunikacja mi(cid:246)dzy w(cid:241)tkiem interfejsu u(cid:276)ytkownika i w(cid:241)tkami roboczymi. Dlatego platforma Android definiuje w(cid:228)asny mechanizm przesy(cid:228)ania komunikatów, s(cid:228)u(cid:276)(cid:241)cy do komunikacji pomi(cid:246)dzy w(cid:241)tkami. W(cid:241)tek interfejsu u(cid:276)ytkownika mo(cid:276)e odci(cid:241)(cid:276)y(cid:232) d(cid:228)ugie zadania przez wy- 56 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę sy(cid:228)anie komunikatów z danymi do przetworzenia w w(cid:241)tkach t(cid:228)a. Mechanizm przesy(cid:228)ania komunikatów jest nieblokuj(cid:241)cym wzorcem konsument – producent, w którym ani w(cid:241)tek producenta, ani w(cid:241)tek konsumenta nie b(cid:246)d(cid:241) blokowa(cid:232) podczas przekazywania komunikatu. Mechanizm obs(cid:228)ugi komunikatów jest fundamentalny w platformie Android, a interfejs API znajduje si(cid:246) w paczce android.os z zestawem klas przedstawionych na rysunku 4.4, które im- plementuj(cid:241) t(cid:246) funkcjonalno(cid:264)(cid:232). Rysunek 4.4. Przegl(cid:241)d interfejsu API Klasa android.os.Looper Dyspozytor komunikatów powi(cid:241)zany z jedynym w(cid:241)tkiem konsumenta. Klasa android.os.Handler Procesor komunikatów w(cid:241)tku konsumenta oraz interfejs dla w(cid:241)tku producenta s(cid:228)u(cid:276)(cid:241)cy do umieszczania komunikatów w kolejce. Obiekt Looper mo(cid:276)e mie(cid:232) wiele powi(cid:241)zanych procedur obs(cid:228)ugi, ale wszystkie umieszczaj(cid:241) komunikaty w tej samej kolejce. Klasa android.os.MessageQueue Nieograniczona lista powi(cid:241)zana komunikatów, które maj(cid:241) by(cid:232) przetworzone w w(cid:241)tku konsumenta. Ka(cid:276)dy obiekt Looper (i Thread) ma co najwy(cid:276)ej jedn(cid:241) kolejk(cid:246) MessageQueue. Klasa android.os.Message Komunikat do wykonania w w(cid:241)tku konsumenta. Komunikaty s(cid:241) wstawiane przez w(cid:241)tki producenta, a przetwarzane przez w(cid:241)tki konsumenta, tak jak pokazano na rysunku 4.5. 1. Wstawianie: w(cid:241)tek producenta umieszcza komunikat w kolejce za pomoc(cid:241) obiektu Handler pod(cid:228)(cid:241)czonego do w(cid:241)tku konsumenta, tak jak opisano w punkcie „Handler” w dalszej cz(cid:246)(cid:264)ci podrozdzia(cid:228)u. 2. Pobieranie: obiekt Looper (opisany w punkcie „Looper” w dalszej cz(cid:246)(cid:264)ci podrozdzia(cid:228)u) dzia(cid:228)a w w(cid:241)tku konsumenta i pobiera komunikaty z kolejki w porz(cid:241)dku sekwencyjnym. 3. Rozsy(cid:228)anie: procedury obs(cid:228)ugi s(cid:241) odpowiedzialne za przetwarzanie komunikatów w w(cid:241)tku konsumenta. W(cid:241)tek mo(cid:276)e mie(cid:232) wiele instancji Handler dla przetwarzania komunikatów. Looper zapewnia, (cid:276)e komunikaty s(cid:241) rozsy(cid:228)ane do w(cid:228)a(cid:264)ciwej instancji Handler. Przyk(cid:293)ad: podstawowe przesy(cid:293)anie komunikatów Zanim wnikliwie przeanalizujemy komponenty, przyjrzyjmy si(cid:246) podstawowemu przyk(cid:228)a- dowi przesy(cid:228)ania komunikatów, aby zapozna(cid:232) si(cid:246) z konfiguracj(cid:241) kodu. Poni(cid:276)szy kod implementuje prawdopodobnie jeden z najbardziej powszechnych przypadków u(cid:276)ycia. U(cid:276)ytkownik naciska na ekranie przycisk, który mo(cid:276)e wywo(cid:228)a(cid:232) d(cid:228)ug(cid:241) operacj(cid:246), tak(cid:241) jak operacja sieciowa. Aby unikn(cid:241)(cid:232) zablokowania renderowania interfejsu u(cid:276)ytkownika, d(cid:228)uga Przesy(cid:293)anie komunikatów w systemie Android (cid:95) 57 Kup książkęPoleć książkę Rysunek 4.5. Przegl(cid:241)d mechanizmu przesy(cid:228)ania komunikatów pomi(cid:246)dzy wieloma w(cid:241)tkami producenta i jednym w(cid:241)tkiem konsumenta. Ka(cid:276)dy komunikat odnosi si(cid:246) do kolejnego komunikatu w kolejce, co na rysunku zosta(cid:228)o oznaczone strza(cid:228)k(cid:241) skierowan(cid:241) w lewo operacja — reprezentowana tutaj przez atrap(cid:246) metody doLongRunningOperation() — musi by(cid:232) wykonana w w(cid:241)tku roboczym. Dlatego konfiguracja ogranicza si(cid:246) jedynie do jednego w(cid:241)tku producenta (w(cid:241)tek interfejsu u(cid:276)ytkownika) i jednego w(cid:241)tku konsumenta (LooperThread). Nasz kod konfiguruje kolejk(cid:246) komunikatów. Obs(cid:228)uguje klikanie przycisku jak zwykle w wywo- (cid:228)aniu zwrotnym metody Click(), które wykonuje w w(cid:241)tku interfejsu u(cid:276)ytkownika. W naszej implementacji wywo(cid:228)anie zwrotne wstawia atrap(cid:246) komunikatu do kolejki komunikatów. Dla zwi(cid:246)z(cid:228)o(cid:264)ci uk(cid:228)ady i komponenty interfejsu u(cid:276)ytkownika zosta(cid:228)y w kodzie pomini(cid:246)te: public class LooperActivity extends Activity { LooperThread mLooperThread; private static class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { if(msg.what == 0) { doLongRunningOperation(); } } }; Looper.loop(); } } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLooperThread = new LooperThread(); mLooperThread.start(); } public void onClick(View v) { 58 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę if (mLooperThread.mHandler != null) { Message msg = mLooperThread.mHandler.obtainMessage(0); mLooperThread.mHandler.sendMessage(msg); } } private void doLongRunningOperation() { // Dodaj tutaj d(cid:225)ugotrwa(cid:225)(cid:261) operacj(cid:266). } protected void onDestroy() { mLooperThread.mHandler.getLooper().quit(); } } Definicja w(cid:241)tku roboczego dzia(cid:228)aj(cid:241)cego jako konsument kolejki komunikatów. Powi(cid:241)zanie obiektu Looper (i po(cid:264)rednio kolejki MessageQueue) z w(cid:241)tkiem. Konfiguracja procedury obs(cid:228)ugi Handler, która b(cid:246)dzie u(cid:276)ywana przez producenta do umieszczania komunikatów w kolejce. Tutaj u(cid:276)ywamy domy(cid:264)lnego konstruktora, który b(cid:246)dzie wi(cid:241)za(cid:228) do obiektu Looper bie(cid:276)(cid:241)cego w(cid:241)tku. Dlatego ten obiekt Handler mo(cid:276)e by(cid:232) utworzony tylko po metodzie Looper.prepare(), bo inaczej nie b(cid:246)dzie si(cid:246) mia(cid:228) z czym wi(cid:241)za(cid:232). Wywo(cid:228)anie zwrotne, które jest uruchamiane, gdy komunikat zostanie przes(cid:228)any do w(cid:241)tku roboczego. Sprawdza parametr what, a nast(cid:246)pnie wykonuje d(cid:228)ug(cid:241) operacj(cid:246). Rozpocz(cid:246)cie rozsy(cid:228)ania komunikatów z kolejki komunikatów do w(cid:241)tku konsumenta. Jest to wywo(cid:228)anie blokuj(cid:241)ce, wi(cid:246)c w(cid:241)tek roboczy nie zako(cid:254)czy wykonywania. Uruchomienie w(cid:241)tku roboczego, aby by(cid:228) gotowy do przetwarzania komunikatów. Istnieje warunek wy(cid:264)cigu mi(cid:246)dzy konfiguracj(cid:241) obiektu mHandler w w(cid:241)tku t(cid:228)a i tym wy- korzystaniem w w(cid:241)tku interfejsu u(cid:276)ytkownika. Dlatego nale(cid:276)y przeprowadzi(cid:232) walidacj(cid:246), czy mHandler jest dost(cid:246)pny. Inicjowanie obiektu Message z argumentem what ustawionym arbitralnie na 0. Umieszczanie komunikatu w kolejce. Zako(cid:254)czenie w(cid:241)tku t(cid:228)a. Wywo(cid:228)anie metody Looper.quit() zatrzymuje rozsy(cid:228)anie komu- nikatów i zwalnia metod(cid:246) Looper.loop() z blokowania, wi(cid:246)c metoda run mo(cid:276)e zako(cid:254)czy(cid:232) wykonywanie, co prowadzi do zamkni(cid:246)cia w(cid:241)tku. Klasy stosowane w przesy(cid:293)aniu komunikatów Przyjrzyjmy si(cid:246) teraz szczegó(cid:228)owo poszczególnym komponentom przesy(cid:228)ania komunikatów i ich zastosowaniu. Klasa MessageQueue Kolejka komunikatów jest reprezentowana przez klas(cid:246) android.os.MessageQueue. Jest ona zbudowana z powi(cid:241)zanych komunikatów stanowi(cid:241)cych nieograniczon(cid:241) jednokierunkow(cid:241) li- st(cid:246) powi(cid:241)zan(cid:241). W(cid:241)tki producenta umieszczaj(cid:241) w niej komunikaty, które b(cid:246)d(cid:241) pó(cid:274)niej rozsy- (cid:228)ane do konsumenta. Komunikaty s(cid:241) sortowane na podstawie znaczników czasu. Komunikat Przesy(cid:293)anie komunikatów w systemie Android (cid:95) 59 Kup książkęPoleć książkę oczekuj(cid:241)cy o najni(cid:276)szej warto(cid:264)ci znacznika czasu jest pierwszy w kolejce do rozes(cid:228)ania do konsumenta. Jednak komunikat jest wysy(cid:228)any tylko wtedy, gdy warto(cid:264)(cid:232) znacznika czasu jest mniejsza ni(cid:276) bie(cid:276)(cid:241)cy czas. Je(cid:264)li nie, wysy(cid:228)ka zostanie wstrzymana do momentu, a(cid:276) bie(cid:276)(cid:241)cy czas przekroczy warto(cid:264)(cid:232) znacznika czasu. Na rysunku 4.6 przedstawiono kolejk(cid:246) komunikatów z trzema oczekuj(cid:241)cymi komunikatami posortowanymi wed(cid:228)ug znaczników czasu, gdzie t1 t2 t3. Tylko jeden komunikat prze- szed(cid:228) barier(cid:246) rozsy(cid:228)ania, któr(cid:241) jest aktualny czas. Kwalifikuj(cid:241)ce si(cid:246) do wys(cid:228)ania komunikaty maj(cid:241) warto(cid:264)(cid:232) znacznika czasu mniejsz(cid:241) ni(cid:276) bie(cid:276)(cid:241)cy czas (na rysunku oznaczony jako „Teraz”). Rysunek 4.6. Komunikaty oczekuj(cid:241)ce w kolejce. Skrajny prawy komunikat jest pierwszy w kolejce do przetworzenia. Strza(cid:228)ki komunikatów oznaczaj(cid:241) referencje do nast(cid:246)pnego komunikatu w kolejce Je(cid:264)li (cid:276)aden komunikat nie przeszed(cid:228) bariery rozsy(cid:228)ania, kiedy Looper jest gotowy do pobrania nast(cid:246)pnego komunikatu, w(cid:241)tek konsumenta blokuje. Wykonywanie zostanie wznowione, gdy tylko komunikat przejdzie przez barier(cid:246) rozsy(cid:228)ania. Producenci mog(cid:241) umieszcza(cid:232) nowe komunikaty w kolejce w dowolnym czasie i na dowolnym miejscu w kolejce. Pozycja umieszczania w kolejce jest oparta na warto(cid:264)ci znacznika czasu. Je(cid:264)li nowy komunikat ma najni(cid:276)sz(cid:241) warto(cid:264)(cid:232) znacznika czasu w porównaniu z komunikatami oczekuj(cid:241)cymi w kolejce, b(cid:246)dzie zajmowa(cid:232) pierwsze miejsce w kolejce, które kwalifikuje go do rozes(cid:228)ania. Umieszczanie komunikatu w kolejce jest zawsze zgodne z kolejno(cid:264)ci(cid:241) sortowania wed(cid:228)ug znacznika czasu. Umieszczanie komunikatu zosta(cid:228)o omówione szczegó(cid:228)owo w punkcie „Handler” w dalszej cz(cid:246)(cid:264)ci rozdzia(cid:228)u. Klasa MessageQueue.IdleHandler Je(cid:264)li nie ma (cid:276)adnych komunikatów do przetworzenia, w(cid:241)tek konsumenta pozostaje przez pewien czas w bezczynno(cid:264)ci. Na rysunku 4.7 zilustrowano szczelin(cid:246) czasow(cid:241), w której w(cid:241)tek konsumenta jest w stanie bezczynno(cid:264)ci. Domy(cid:264)lnie w czasie bezczynno(cid:264)ci w(cid:241)tek konsumenta po prostu czeka na nowe komunikaty. Jednak zamiast oczekiwa(cid:232), w tych okresach bezczynno(cid:264)ci w(cid:241)tek mo(cid:276)e by(cid:232) wykorzystywany do wykonywania innych zada(cid:254). Ta funkcja mo(cid:276)e by(cid:232) wy- korzystana do umo(cid:276)liwienia od(cid:228)o(cid:276)enia wykonywania niekrytycznych zada(cid:254) do momentu, a(cid:276) (cid:276)adne inne komunikaty nie b(cid:246)d(cid:241) konkurowa(cid:232) o czas wykonywania. 60 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę Rysunek 4.7. Je(cid:264)li (cid:276)aden komunikat nie przeszed(cid:228) przez barier(cid:246) rozsy(cid:228)ania, tworzy si(cid:246) okno czasowe, które mo(cid:276)e by(cid:232) wykorzystane do wykonywania zada(cid:254), dopóki nie b(cid:246)dzie wymagane wykonanie nast(cid:246)pnego komunikatu oczekuj(cid:241)cego Gdy komunikat oczekuj(cid:241)cy zostanie wys(cid:228)any i (cid:276)aden inny komunikat nie przeszed(cid:228) przez barier(cid:246) rozsy(cid:228)ania, tworzy si(cid:246) okno czasowe, w którym w(cid:241)tek konsumenta mo(cid:276)e by(cid:232) wyko- rzystany do wykonywania innych zada(cid:254). Aplikacja zdobywa to okno czasowe za pomoc(cid:241) interfej- su android.os.MessageQueue.IdleHandler. Jest to nas(cid:228)uchiwacz, który generuje wywo(cid:228)a- nia zwrotne, gdy w(cid:241)tek konsumenta jest w stanie bezczynno(cid:264)ci. Nas(cid:228)uchiwacz jest do(cid:228)(cid:241)czany do kolejki MessageQueue i od(cid:228)(cid:241)czany od niej za pomoc(cid:241) nast(cid:246)puj(cid:241)cych wywo(cid:228)a(cid:254): // Uzyskanie kolejki komunikatów bie(cid:298)(cid:261)cego w(cid:261)tku. MessageQueue mq = Looper.myQueue(); // Tworzenie i rejestrowanie nas(cid:225)uchiwacza bezczynno(cid:286)ci. MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler(); mq.addIdleHandler(idleHandler) // Wyrejestrowanie nas(cid:225)uchiwacza bezczynno(cid:286)ci. mq.removeIdleHandler(idleHandler) Interfejs procedury obs(cid:228)ugi bezczynno(cid:264)ci (ang. idle handler) sk(cid:228)ada si(cid:246) z tylko jednego wywo- (cid:228)ania zwrotnego metody: interface IdleHandler { boolean queueIdle(); } Gdy kolejka komunikatów wykrywa czas bezczynno(cid:264)ci dla w(cid:241)tku konsumenta, wywo(cid:228)uje metod(cid:246) queueIdle() na wszystkich zarejestrowanych instancjach IdleHandler. To do apli- kacji nale(cid:276)y odpowiedzialne zaimplementowanie wywo(cid:228)ania zwrotnego. Zwykle nale(cid:276)y unika(cid:232) d(cid:228)ugotrwa(cid:228)ych zada(cid:254), poniewa(cid:276) podczas wykonywania b(cid:246)d(cid:241) one opó(cid:274)nia(cid:232) komunikaty ocze- kuj(cid:241)ce. Implementacja metody queueIdle() musi zwraca(cid:232) poni(cid:276)sze warto(cid:264)ci logiczne: Warto(cid:264)(cid:232) true (prawda) Interfejs IdleHandler jest utrzymywany w stanie aktywno(cid:264)ci. Kontynuuje odbieranie wywo(cid:228)a(cid:254) zwrotnych dla kolejnych okien czasowych bezczynno(cid:264)ci. Przesy(cid:293)anie komunikatów w systemie Android (cid:95) 61 Kup książkęPoleć książkę Warto(cid:264)(cid:232) false (fa(cid:228)sz) Interfejs IdleHandler jest nieaktywny. Nie b(cid:246)dzie wi(cid:246)cej odbiera(cid:228) wywo(cid:228)a(cid:254) zwrotnych dla kolejnych okien czasowych bezczynno(cid:264)ci. To jest to samo co usuni(cid:246)cie nas(cid:228)uchiwacza poprzez metod(cid:246) MessageQueue.removeIdleHandler(). Przyk(cid:293)ad: u(cid:348)ycie interfejsu IdleHandler do zako(cid:295)czenia niewykorzystywanego w(cid:233)tku Wszystkie zarejestrowane interfejsy IdleHandler dla kolejek MessageQueue s(cid:241) wywo(cid:228)ywane, gdy w(cid:241)tek znajduje si(cid:246) w oknie czasowej bezczynno(cid:264)ci, gdzie czeka na nowe komunikaty do przetworzenia. Okna bezczynno(cid:264)ci mog(cid:241) wyst(cid:241)pi(cid:232) przed pierwszym komunikatem, pomi(cid:246)- dzy komunikatami oraz po ostatnim komunikacie. Je(cid:264)li wiele producentów zawarto(cid:264)ci b(cid:246)dzie przetwarza(cid:232) dane sekwencyjnie w w(cid:241)tku konsumenta, interfejs IdleHandler mo(cid:276)e by(cid:232) wy- korzystywany do zako(cid:254)czenia w(cid:241)tku konsumenta, gdy wszystkie komunikaty zosta(cid:228)y przetwo- rzone, aby niewykorzystywany w(cid:241)tek nie pozostawa(cid:228) w pami(cid:246)ci. Przy zastosowaniu interfejsu IdleHandler nie jest konieczne (cid:264)ledzenie ostatniego umieszczonego komunikatu, aby wiedzie(cid:232), kiedy w(cid:241)tek mo(cid:276)e zosta(cid:232) zako(cid:254)czony. Ten przypadek u(cid:276)ycia ma zastosowanie tylko wtedy, gdy w(cid:241)tki produkuj(cid:241)ce umiesz- czaj(cid:241) komunikaty w kolejce MessageQueue bez opó(cid:274)nienia, tak aby w(cid:241)tek konsu- menta nigdy nie znajdowa(cid:228) si(cid:246) w stanie bezczynno(cid:264)ci, dopóki nie zostanie wstawiony ostatni komunikat. Metoda ConsumeAndQuitThread przedstawia struktur(cid:246) konsumowania w(cid:241)tku z obiektami Looper i MessageQueue, która ko(cid:254)czy w(cid:241)tek, gdy nie ma wi(cid:246)cej komunikatów do przetworzenia: public class ConsumeAndQuitThread extends Thread implements MessageQueue.IdleHandler { private static final String THREAD_NAME = ConsumeAndQuitThread ; public Handler mConsumerHandler; private boolean mIsFirstIdle = true; public ConsumeAndQuitThread() { super(THREAD_NAME); } @Override public void run() { Looper.prepare(); mConsumerHandler = new Handler() { @Override public void handleMessage(Message msg) { // Konsumowanie danych. } }; Looper.myQueue().addIdleHandler(this); Looper.loop(); } @Override public boolean queueIdle() { if (mIsFirstIdle) { mIsFirstIdle = false; return true; } 62 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę mConsumerHandler.getLooper().quit(); return false; } public void enqueueData(int i) { mConsumerHandler.sendEmptyMessage(i); } } Rejestrowanie interfejsu IdleHandler w w(cid:241)tku t(cid:228)a, kiedy zostaje on uruchomiony, a Looper jest przygotowany, wi(cid:246)c skonfigurowana zostaje kolejka MessageQueue. Pierwsze wywo(cid:228)anie metody queueIdle powinno przej(cid:264)(cid:232), poniewa(cid:276) wyst(cid:246)puje przed odebraniem pierwszego komunikatu. Zwraca warto(cid:264)(cid:232) true przy pierwszym wywo(cid:228)aniu, aby interfejs IdleHandler pozosta(cid:228) za- rejestrowany. Zako(cid:254)czenie w(cid:241)tku. Umieszczanie komunikatów jest przeprowadzane z wielu w(cid:241)tków jednocze(cid:264)nie, z symulacj(cid:241) losowo(cid:264)ci czasu wstawiania: final ConsumeAndQuitThread consumeAndQuitThread = new ConsumeAndQuitThread(); consumeAndQuitThread.start(); for (int i = 0; i 10; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i 10; i++) { SystemClock.sleep(new Random().nextInt(10)); consumeAndQuitThread.enqueueData(i); } } }).start(); Komunikaty Ka(cid:276)dy element w MessageQueue pochodzi z klasy android.os.Message. Jest to obiekt konte- nera przenosz(cid:241)cy element danych lub zadanie, ale nigdy obie te rzeczy. Dane s(cid:241) przetwarza- ne przez w(cid:241)tek konsumenta, podczas gdy zadanie jest po prostu wykonywane, gdy zostanie usuni(cid:246)te z kolejki i nie ma innego przetwarzania do przeprowadzenia. Komunikat zna swój procesor odbiorczy (tj. Handler) i mo(cid:276)e sam si(cid:246) kolejkowa(cid:232) za pomoc(cid:241) metody Message.sendToTarget(): Message m = Message.obtain(handler, runnable); m.sendToTarget(); Jak zobaczysz w punkcie „Handler” w dalszej cz(cid:246)(cid:264)ci podrozdzia(cid:228)u, procedura obs(cid:228)ugi jest najcz(cid:246)(cid:264)ciej u(cid:276)ywana do kolejkowania komunikatów, poniewa(cid:276) oferuje wi(cid:246)ksz(cid:241) elastyczno(cid:264)(cid:232) w odniesieniu do umieszczania komunikatów w kolejce. Komunikat danych Zestaw danych zawiera wiele parametrów, które mog(cid:241) by(cid:232) przekazywane do w(cid:241)tku kon- sumenta, tak jak to przedstawiono w tabeli 4.2. Przesy(cid:293)anie komunikatów w systemie Android (cid:95) 63 Kup książkęPoleć książkę Tabela 4.2. Parametry komunikatu Nazwa parametru what arg1, arg2 Typ int int obj Object data replyTo Bundle Messenger callback Runnable Zastosowanie Identyfikator komunikatu. Przekazuje intencj(cid:253) komunikatu. Proste warto(cid:316)ci danych s(cid:293)u(cid:348)(cid:233)ce do obs(cid:293)ugi powszechnego przypadku u(cid:348)ycia, jakim jest przekazywanie liczb ca(cid:293)kowitych. Je(cid:316)li do konsumenta maj(cid:233) by(cid:235) przekazane maksymalnie dwie warto(cid:316)ci ca(cid:293)kowite, te parametry s(cid:233) bardziej efektywne ni(cid:348) alokowanie typu Bundle, co zosta(cid:293)o obja(cid:316)nione poni(cid:348)ej przy parametrze data. Obiekt arbitralny. Je(cid:316)li obiekt jest przekazywany do w(cid:233)tku w innym procesie, musi implementowa(cid:235) interfejs Parcelable. Kontener arbitralnych warto(cid:316)ci danych. Referencja do obiektu Handler w innym procesie. Umo(cid:348)liwia wymian(cid:253) komunikatów mi(cid:253)dzy procesami, tak jak zosta(cid:293)o to opisane w rozdziale 5., w punkcie „Komunikacja dwukierunkowa”. Zadanie, które ma by(cid:235) wykonane w w(cid:233)tku. Jest to pole wewn(cid:253)trznej instancji, które przechowuje obiekt Runnable z metod Handler.post, tak jak zosta(cid:293)o to opisane w punkcie „Handler” w dalszej cz(cid:253)(cid:316)ci podrozdzia(cid:293)u. Komunikat zadania Zadanie jest reprezentowane przez obiekt java.lang.Runnable, który ma by(cid:232) wykonany w w(cid:241)tku konsumenta. Komunikaty zada(cid:254) nie mog(cid:241) zawiera(cid:232) (cid:276)adnych danych poza samym zadaniem. Kolejka MessageQueue mo(cid:276)e zawiera(cid:232) dowoln(cid:241) kombinacj(cid:246) komunikatów danych i zada(cid:254). W(cid:241)tek konsumenta przetwarza je w sposób sekwencyjny, niezale(cid:276)nie od rodzaju. Je(cid:264)li komuni- kat jest komunikatem danych, konsument przetwarza te dane. Komunikaty zada(cid:254) s(cid:241) obs(cid:228)ugiwane przez umo(cid:276)liwienie wykonania obiektu Runnable w w(cid:241)tku konsumenta, ale w(cid:241)tek konsumenta nie otrzymuje komunikatu, który ma by(cid:232) przetworzony w metodzie Handler.handleMessage (cid:180)(Message), jak ma to miejsce w przypadku komunikatów danych. Cykl (cid:276)ycia komunikatu jest prosty: producent tworzy komunikat, który ostatecznie jest prze- twarzany przez konsumenta. Ten opis jest wystarczaj(cid:241)cy dla wi(cid:246)kszo(cid:264)ci przypadków u(cid:276)ycia, ale kiedy pojawia si(cid:246) problem, g(cid:228)(cid:246)bsze zrozumienie obs(cid:228)ugi komunikatów jest bezcenne. Rzu(cid:232)my okiem na to, co faktycznie dzieje si(cid:246) z komunikatem podczas jego cyklu (cid:276)ycia, który mo(cid:276)na podzieli(cid:232) na cztery g(cid:228)ówne stany, tak jak przedstawiono na rysunku 4.8. (cid:263)rodowisko uruchomieniowe przechowuje obiekty komunikatów w puli ca(cid:228)ej aplikacji, aby umo(cid:276)liwi(cid:232) ponowne wykorzystywanie poprzednich komunikatów. Pozwala to unikn(cid:241)(cid:232) narzutu tworzenia nowych instancji dla ka(cid:276)dego przekazania. Czas wykonywania obiektu komunikatu jest zwykle bardzo krótki, a w jednostce czasu przetwarzanych jest wiele komunikatów. Rysunek 4.8. Stany cyklu (cid:276)ycia komunikatu Transfery stanu s(cid:241) cz(cid:246)(cid:264)ciowo kontrolowane przez aplikacj(cid:246), a cz(cid:246)(cid:264)ciowo przez platform(cid:246). Nale(cid:276)y zauwa(cid:276)y(cid:232), (cid:276)e stany nie s(cid:241) obserwowalne, a aplikacja nie mo(cid:276)e (cid:264)ledzi(cid:232) przej(cid:264)(cid:232) z jed- nego stanu do innego (chocia(cid:276) istniej(cid:241) sposoby (cid:264)ledzenia ruchu komunikatów, które zosta(cid:228)y 64 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę opisane w punkcie „Obserwowanie kolejki komunikatów” w dalszej cz(cid:246)(cid:264)ci podrozdzia(cid:228)u). Dlatego aplikacja nie powinna dokonywa(cid:232) (cid:276)adnych za(cid:228)o(cid:276)e(cid:254) na temat bie(cid:276)(cid:241)cego stanu pod- czas obs(cid:228)ugiwania komunikatu. Zainicjowany W stanie zainicjowanym utworzony zosta(cid:228) obiekt komunikatu ze stanem mutowalnym, a je(cid:264)li jest to komunikat danych, zosta(cid:228) wype(cid:228)niony danymi. Aplikacja jest odpowiedzialna za two- rzenie obiektu komunikatu przy u(cid:276)yciu jednego z poni(cid:276)szych wywo(cid:228)a(cid:254), które bior(cid:241) obiekt z puli obiektów: (cid:120) bezpo(cid:264)rednie konstruowanie obiektu: Message m = new Message(); (cid:120) metody fabryki: (cid:120) pusty komunikat: Message m = Message.obtain(); (cid:120) komunikat danych: Message m = Message.obtain(Handler h); Message m = Message.obtain(Handler h, int what); Message m = Message.obtain(Handler h, int what, Object o); Message m = Message.obtain(Handler h, int what, int arg1, int arg2); Message m = Message.obtain(Handler h, int what, int arg1, int arg2, Object o); (cid:120) komunikat zadania: Message m = Message.obtain(Handler h, Runnable task); (cid:120) konstruktor kopiowania: Message m = Message.obtain(Message originalMsg); Oczekuj(cid:233)cy Komunikat zosta(cid:228) umieszczony w kolejce przez w(cid:241)tek producenta i czeka na wys(cid:228)anie do w(cid:241)tku konsumenta. Wys(cid:293)any W tym stanie Looper pobra(cid:228) i usun(cid:241)(cid:228) komunikat z kolejki. Komunikat zosta(cid:228) wys(cid:228)any do w(cid:241)tku konsumenta i jest aktualnie przetwarzany. Nie ma interfejsu API aplikacji dla tej ope- racji, poniewa(cid:276) rozsy(cid:228)anie jest kontrolowane przez Looper bez wp(cid:228)ywu na aplikacj(cid:246). Kiedy Looper wysy(cid:228)a komunikat, sprawdza informacje o dor(cid:246)czeniu komunikatu i dostarcza ko- munikat do w(cid:228)a(cid:264)ciwego odbiorcy. Po wys(cid:228)aniu komunikat jest wykonywany w w(cid:241)tku kon- sumenta. Odzyskany W tym momencie w cyklu (cid:276)ycia stan komunikatu zostaje wyczyszczony i instancja zostaje zwrócona do puli komunikatów. Looper obs(cid:228)uguje recykling komunikatu po zako(cid:254)czeniu jego wykonywania w w(cid:241)tku konsumenta. Recykling komunikatów jest obs(cid:228)ugiwany przez (cid:264)rodowi- sko uruchomieniowe i nie powinien by(cid:232) przeprowadzany bezpo(cid:264)rednio przez aplikacj(cid:246). Przesy(cid:293)anie komunikatów w systemie Android (cid:95) 65 Kup książkęPoleć książkę Gdy komunikat zostaje umieszczony w kolejce, jego zawarto(cid:264)(cid:232) nie powinna by(cid:232) zmieniana. Teoretycznie mo(cid:276)na zmienia(cid:232) zawarto(cid:264)(cid:232) przed wys(cid:228)aniem komunikatu. Jednak poniewa(cid:276) stan nie jest obserwowalny, komunikaty mog(cid:241) by(cid:232) przetwarzane przez w(cid:241)tek konsumenta w czasie, kiedy producent próbuje zmieni(cid:232) dane, co wp(cid:228)y- wa na bezpiecze(cid:254)stwo w(cid:241)tku. By(cid:228)oby jeszcze gorzej, je(cid:264)li komunikat zosta(cid:228)by odzy- skany, poniewa(cid:276) potem zosta(cid:228)by zwrócony do puli komunikatów i ewentualnie wy- korzystany przez innego producenta do przekazywania danych w innej kolejce. Looper Klasa android.os.Looper obs(cid:228)uguje wysy(cid:228)k(cid:246) komunikatów z kolejki do powi(cid:241)zanej proce- dury obs(cid:228)ugi. Wszystkie komunikaty, które przesz(cid:228)y przez barier(cid:246) rozsy(cid:228)ania (tak jak poka- zano na rysunku 4.6), kwalifikuj(cid:241) si(cid:246) do wys(cid:228)ania przez Looper. Dopóki kolejka ma komuni- katy kwalifikuj(cid:241)ce si(cid:246) do rozes(cid:228)ania, Looper zapewnia, (cid:276)e w(cid:241)tek konsumenta b(cid:246)dzie odbiera(cid:228) komunikaty. Gdy nie ma komunikatów, które przesz(cid:228)y przez barier(cid:246) rozsy(cid:228)ania, w(cid:241)tek kon- sumenta b(cid:246)dzie blokowa(cid:228), a(cid:276) jaki(cid:264) komunikat przejdzie barier(cid:246) rozsy(cid:228)ania. W(cid:241)tek konsumenta nie wchodzi w bezpo(cid:264)redni(cid:241) interakcj(cid:246) z kolejk(cid:241) komunikatów, aby po- biera(cid:232) komunikaty. Zamiast tego kolejka komunikatów jest dodawana do w(cid:241)tku, kiedy zo- staje do(cid:228)(cid:241)czony Looper. Looper zarz(cid:241)dza kolejk(cid:241) komunikatów i u(cid:228)atwia rozsy(cid:228)anie komuni- katów do w(cid:241)tku konsumenta. Domy(cid:264)lnie tylko w(cid:241)tek interfejsu u(cid:276)ytkownika posiada obiekt Looper. W(cid:241)tki utworzone w apli- kacji musz(cid:241) bezpo(cid:264)rednio powi(cid:241)za(cid:232) Looper. Gdy dla w(cid:241)tku tworzony jest Looper, jest on pod(cid:228)(cid:241)czany do kolejki komunikatów. Looper dzia(cid:228)a jako po(cid:264)rednik mi(cid:246)dzy kolejk(cid:241) a w(cid:241)tkiem. Konfiguracja odbywa si(cid:246) w metodzie run w(cid:241)tku: class ConsumerThread extends Thread { @Override public void run() { Looper.prepare(); // Pomini(cid:266)te tworzenie obiektu Handler. Looper.loop(); Pierwszym krokiem jest utworzenie obiektu Looper, co odbywa si(cid:246) za pomoc(cid:241) statycznej metody prepare(). Spowoduje to utworzenie kolejki komunikatów i powi(cid:241)zanie jej z bie- (cid:276)(cid:241)cym w(cid:241)tkiem. W tym momencie kolejka komunikatów jest gotowa do umieszczania w niej komunikatów, ale nie zosta(cid:228)y one jeszcze rozes(cid:228)ane do w(cid:241)tku konsumenta. Rozpocz(cid:246)cie obs(cid:228)ugi komunikatów w kolejce komunikatów. Jest to metoda blokuj(cid:241)ca, która zapewnia, (cid:276)e metoda run() nie zostanie zako(cid:254)czona. Podczas gdy metoda run() blokuje, Looper wysy(cid:228)a komunikaty do w(cid:241)tku konsumenta w celu przetworzenia. W(cid:241)tek mo(cid:276)e mie(cid:232) tylko jeden powi(cid:241)zany obiekt Looper. Je(cid:264)li aplikacja spróbuje skonfiguro- wa(cid:232) drugi, wyst(cid:241)pi b(cid:228)(cid:241)d wykonywania aplikacji (ang. runtime error). W konsekwencji w(cid:241)tek mo(cid:276)e mie(cid:232) tylko jedn(cid:241) kolejk(cid:246) komunikatów, co oznacza, (cid:276)e komunikaty wysy(cid:228)ane przez wiele w(cid:241)tków producenta s(cid:241) przetwarzane sekwencyjnie w w(cid:241)tku konsumenta. Dlatego ak- tualnie wykonywany komunikat od(cid:228)o(cid:276)y wykonywanie kolejnych komunikatów do czasu, a(cid:276) zostanie przetworzony. Komunikaty z d(cid:228)ugim czasem wykonywania nie b(cid:246)d(cid:241) u(cid:276)yte, je(cid:276)eli mog(cid:241) opó(cid:274)ni(cid:232) inne wa(cid:276)ne zadania w kolejce. 66 (cid:95) Rozdzia(cid:293) 4. Komunikacja w(cid:233)tków Kup książkęPoleć książkę Zako(cid:295)czenie obiektu Looper Zatrzymanie przetwarzania komunikatów przez Looper odbywa si(cid:246) za pomoc(cid:241) metod quit lub quitSafely. Metoda quit() zatrzymuje wysy(cid:228)anie przez Looper kolejnych komunikatów z kolejki. Wszystkie komunikaty oczekuj(cid:241)ce w kolejce, w tym te, które przesz(cid:228)y przez barier(cid:246) rozsy(cid:228)ania, zostan(cid:241) porzucone. Z drugiej strony metoda quitSafely porzuca tylko komuni- katy, które nie przesz(cid:228)y przez barier(cid:246) rozsy(cid:228)ania. Oczekuj(cid:241)ce komunikaty, które zosta(cid:228)y za- kwalifikowane do wysy(cid:228)ki, zostan(cid:241) przetworzone przed zatrzymaniem obiektu Looper. Metod(cid:246) quitSafely dodano w interfejsie API poziomu 18 (Jelly Bean 4.3). Dotych- czasowe poziomy API obs(cid:228)ugiwa(cid:228)y tylko metod(cid:246) quit. Zako(cid:254)czenie obiektu Looper nie ko(cid:254)czy w(cid:241)tku. Nast(cid:246)puje jedynie wyj(cid:264)cie z metody Looper. (cid:180)loop() i w(cid:241)tek mo(cid:276)e wznowi(cid:232) dzia(cid:228)anie w metodzie, która wywo(cid:228)a(cid:228)a metod(cid:246) loop. Jednak nie mo(cid:276)na uruchomi(cid:232) starego lub nowego obiektu Looper, wi(cid:246)c w(cid:241)tek nie mo(cid:276)e ju(cid:276) kolejkowa(cid:232) ani obs(cid:228)ugiwa(cid:232) komunikatów. Je(cid:264)li wywo(cid:228)asz metod(cid:246) Looper.prepare
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Android. Aplikacje wielowątkowe. Techniki przetwarzania
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: