Zaklinowanie się IntentService

Zbudowałem aplikację, która używa IntentService do manipulowania bazą danych aplikacji i wywoływania usług internetowych.

Przykład:

Mam działanie, które jest listą przedmiotów. Ta lista jest wypełniana przez CursorAdapter. Każda pozycja ma pole wyboru, a lista jest uporządkowana według zaznaczonych (niezaznaczone elementy na górze i zaznaczone elementy na dole). Gdy pole wyboru elementu jest zaznaczone, wysyłam intencję do mojego IntentService, informując go, aby oznaczył zaznaczoną kolumnę w tabeli bazy danych dla tego elementu, a następnie wiadomość jest rozgłaszana, Aktywność widzi ją i ponownie wysyła zapytanie do Kursora.

Kiedy używam urządzenia mobilnego i zaczynam szybko sprawdzać pola wyboru, aplikacja zasadniczo się zawiesza. Interfejs użytkownika nie zawiesza się, ale IntentService w tle tak. Mogę to stwierdzić, ponieważ Kursor na ekranie nie jest odświeżany.

Rozumiem, że intencje przekazywane do IntentServices nie są przetwarzane asynchronicznie. Czy to jest poprawne? Zachowuje się prawie tak, jakbym musiał używać semaforów, ale nie mogę zrozumieć, dlaczego intencje są kolejkowane i przetwarzane indywidualnie.

Załóżmy, że zaznaczę 3 pola wyboru; więc w kolejce są 3 intencje. Po pierwszym wysyłam wiadomość, którą otrzymuje Aktywność, nakazując jej ponowne zapytanie o kursor. Czy potencjalny problem może wynikać z tego, że ponownie wysyłam zapytanie do kursora w tym samym czasie, w którym przetwarzam drugą intencję (aktualizowanie innego wiersza w tabeli)? Zapytania kursora mają miejsce w wątku interfejsu użytkownika.

^ Myślę, że to jest mój problem. Czego powinienem użyć, aby to zsynchronizować? Semafory? Czy ktoś może wskazać mi dokumentację/przykłady?

Innym potencjalnym problemem jest to, co jeśli moja aktywność zarządza kursorem na stole; również moja IntentService wysyła zapytanie do Cursor z tej samej tabeli. Mam przypadki, w których tak się dzieje, gdy moja IntentService przechodzi przez każdy wiersz w Cursor, aby znaleźć elementy do wysłania do usługi sieci Web. Chociaż nie sądziłem, że będzie to problem, dopóki kursory nie będą wysyłać zapytań w tym samym czasie?

Czy ktoś ma inne pomysły?


person Andrew    schedule 06.10.2010    source źródło
comment
Kilka szybkich przemyśleń: dodałbym logowanie do każdej części systemu, aby naprawdę dowiedzieć się, gdzie jest dżem. Czy warstwa dostępu do bazy danych to ContentProvider (nie są zaprojektowane do wielowątkowości)? Ponadto po co używać IntentService, gdy funkcja obsługi w działaniu wykonałaby zadanie? Podobnie, po co martwić się publikowaniem aktualizacji asynchronicznie, jeśli działanie i tak wysyła zapytanie do bazy danych w głównym wątku, a nie (powiedzmy) AsyncQueryHandler?   -  person Christopher Orr    schedule 06.10.2010
comment
Tak, mój dostęp do bazy danych odbywa się za pośrednictwem ContentProvider. Zajrzę do AsyncQueryHandler   -  person Andrew    schedule 07.10.2010


Odpowiedzi (1)


Gdy pole wyboru elementu jest zaznaczone, wysyłam intencję do mojego IntentService, informując go, aby oznaczył zaznaczoną kolumnę w tabeli bazy danych dla tego elementu, a następnie wiadomość jest rozgłaszana, Aktywność widzi ją i ponownie wysyła zapytanie do Kursora.

Ma to wrażenie "używania Buicka do pacnięcia muchy".

Rozumiem, że intencje przekazywane do IntentServices nie są przetwarzane asynchronicznie. Czy to jest poprawne?

onHandleIntent() w Twoim IntentService jest wywoływana w wątku w tle, a startService() jest zawsze asynchroniczna.

Zachowuje się prawie tak, jakbym musiał używać semaforów, ale nie mogę zrozumieć, dlaczego intencje są kolejkowane i przetwarzane indywidualnie.

Poszczególne Intents są umieszczane w kolejce i przetwarzane szeregowo przez IntentService.

Czy potencjalny problem może wynikać z tego, że ponownie wysyłam zapytanie do kursora w tym samym czasie, w którym przetwarzam drugą intencję (aktualizowanie innego wiersza w tabeli)?

To z pewnością nie pomaga.

Czego powinienem użyć, aby to zsynchronizować? Semafory?

Po prostu użyłbym synchronized(someStaticDataMember), ale semafory powinny działać.

Chociaż nie sądziłem, że będzie to problem, dopóki kursory nie będą wysyłać zapytań w tym samym czasie?

Rzeczywiste zapytanie nie jest wykonywane, dopóki nie zostanie dotknięty Cursor (np. moveToFirst(), getCount()). Następnie, o ile zestaw wyników zapytania jest mniejszy niż 1 MB, wyniki znajdują się w całości w Cursor.

Częściowo zgadzam się z Christopherem: użyj trochę logowania, aby dowiedzieć się dokładnie, co się dzieje, i rozważ nieco uproszczenie (np. AsyncTask do zapisu bazy danych zamiast IntentService).

person CommonsWare    schedule 06.10.2010
comment
Co się stanie, jeśli zainicjuję wątek do zapisania w bazie danych, ale działanie zostanie odzyskane przed zakończeniem wątku? Rozumiem, że prawdopodobieństwo jest odległe, ponieważ pisanie jest szybką operacją, ale powiedz, że piszę tablicę obiektów. Te dane są powiązane z Aktywnością i jeśli Aktywność zostanie odzyskana, mój wątek ma kłopoty. Czy możesz wskazać mi przykład synchronizacji statycznego elementu członkowskiego? Zrobiłem to na konstruktorach singleton, ale nie jestem pewien, jak owinąć to wokół dwóch oddzielnych bloków kodu. - person Andrew; 07.10.2010
comment
Powinienem również zauważyć, że moje wstawki/aktualizacje/usunięcia bazy danych znajdują się w IntentService z powodu obejrzanego przeze mnie filmu. Jest film prezentacyjny Google I/O 2010, w którym inżynier Google opowiada o modelach tworzenia aplikacji wymagających dużej liczby usług internetowych. Twierdzi, że długotrwałe operacje, w tym wywołania bazy danych, powinny mieć miejsce w Usłudze. - person Andrew; 07.10.2010
comment
@Andrew: jeśli aktywność zostanie odzyskana, mój wątek ma kłopoty – dlaczego? AsyncTask zostanie ukończony. Czy możesz wskazać mi przykład synchronizacji statycznego elementu członkowskiego? -- nie do końca, to trywialna Java. Jeśli potrafisz napisać IntentService, użycie słowa kluczowego synchronized() powinno być spacerkiem po parku! Twierdzi, że długotrwałe operacje, w tym wywołania bazy danych, powinny mieć miejsce w Usłudze. -- Byłem tam. IntentService nie jest jedynym wzorcem wykonywania pracy poza głównym wątkiem aplikacji, a Googler i ja po prostu nie zgadzamy się w niektórych kwestiach. - person CommonsWare; 07.10.2010
comment
@Andrew: I nie mówię, że musisz odejść od używania IntentService, tylko że nie użyłbym IntentService do czegoś tak trywialnego jak prosta operacja na bazie danych. - person CommonsWare; 07.10.2010
comment
Załóżmy, że mam zmienne członkowskie należące do Activity, które przechowują niektóre dane. Chcę zapisać te kawałki do bazy danych. Uruchamiam zadanie asynchroniczne, aby je zapisać, i podczas wykonywania zadania asynchronicznego Aktywność jest odzyskiwana przez system lub niszczona (obrót ekranu). Czy nie spowodowałoby to problemu? - person Andrew; 07.10.2010
comment
Jak mam nie uzyskać dostępu do danych z głównej aplikacji, gdy tam znajdują się dane i to jest to, co muszę zapisać? A może sugerujesz skopiowanie tych danych do zmiennych w operacji AsyncTask, a operacja AsyncTask działa na tych zmiennych? - person Andrew; 07.10.2010
comment
@Andrew: Jak mam nie uzyskać dostępu do danych z głównej aplikacji, kiedy tam znajdują się dane i to jest to, czego potrzebuję? -- Activity to klasa, a nie aplikacja. A może sugerujesz skopiowanie tych danych do zmiennych w operacji AsyncTask, a operacja AsyncTask działa na tych zmiennych? -- tak, skopiuj dane do AsyncTask, tak jak kopiujesz dane do Intent używanego z IntentService. - person CommonsWare; 07.10.2010
comment
Przykro mi. Często czytam Aktywność, gdy widzę aplikację. Czy naprawdę powinienem uczynić to AsyncTask statycznym? Załóżmy, że użytkownik wykonuje kilka czynności, z których każda wymaga zapisu do bazy danych. Załóżmy, że jedna z tych akcji jest wywoływana przed zakończeniem operacji wynikowej z poprzedniej akcji przez AsyncTask? - person Andrew; 07.10.2010
comment
@Andrew: To zupełnie osobny problem, który napotkasz niezależnie od tego, jak wykonujesz operacje na bazie danych (AsyncTask, IntentService itd.). Typowym podejściem jest blokowanie danych wejściowych użytkownika, które mogą mieć wpływ na bazę danych podczas operacji we/wy bazy danych. Jest to jeden z powodów, dla których możesz dokonać przedwczesnej optymalizacji. Tylko testując swoją aplikację, będziesz w stanie określić, czy naprawdę musisz wykonać niektóre lub wszystkie zapisy do bazy danych w tle, co powoduje cały ten ból głowy. Pamiętaj, aby przetestować sprzęt (flash I/O różni się od I/O dysku twardego). - person CommonsWare; 07.10.2010
comment
Ale blokowanie danych wejściowych użytkownika podczas operacji we/wy bazy danych nie jest w rzeczywistości akceptowalne, prawda? W tym momencie możesz równie dobrze uruchomić wszystko w głównym wątku. Czy kolejka IntentService nie ma zamiarów do przetworzenia po zakończeniu przetwarzania postępującego Intent? (Wiem, że zadawałem to pytanie wcześniej, przepraszam, chcę się tylko upewnić). W każdym razie, jeśli operacje we/wy bazy danych narastają, czy nie byłoby właściwe umieszczenie tego elementu w usłudze? Nie mam nic przeciwko używaniu AsyncTask. Chcę tylko użyć tego, co ostatecznie będzie działać z wieloma częstymi wywołaniami bazy danych. - person Andrew; 07.10.2010
comment
@Andrew: Ale blokowanie danych wejściowych użytkownika podczas operacji we/wy bazy danych nie jest do przyjęcia, prawda? -- przez większość czasu operacje wejścia/wyjścia bazy danych trwają kilka milisekund. Ponownie, tu właśnie wchodzi w grę testowanie. Czy kolejka IntentService nie ma zamiarów do przetworzenia po zakończeniu przetwarzania postępującego Intent? -- tak. W każdym razie, jeśli operacje we/wy bazy danych narastają, czy nie byłoby właściwe umieszczenie tego elementu w usłudze? -- istnieje możliwość, ale nie jest to bezwzględny wymóg. - person CommonsWare; 07.10.2010
comment
Ok. Zobaczę, co uda mi się połączyć z tymi informacjami. Dziękuję Ci za całą twoją pomoc! (z tym pytaniem i innymi) - person Andrew; 07.10.2010