Handler vs AsyncTask vs Thread [zamknięte]

382

Dostałem nieco mylić o różnicach między Handlers, AsyncTaska Threadsw Androidzie. Przeczytałem sporo blogów i pytań tutaj w StackOverflow.

Handlerto wątki w tle, które zapewniają komunikację z interfejsem użytkownika. Na przykład aktualizacja paska postępu powinna odbywać się za pośrednictwem Handler. Korzystanie z programów obsługi ma tę zaletę MessagingQueues, że jeśli chcesz planować wiadomości lub aktualizować wiele elementów interfejsu użytkownika lub wykonywać powtarzające się zadania.

AsyncTasksą podobne, w rzeczywistości korzystają z nich Handler, ale nie działają w wątku interfejsu użytkownika, więc dobrze nadaje się do pobierania danych, na przykład usług sieciowych. Później możesz wchodzić w interakcje z interfejsem użytkownika.

Threadnie może jednak wchodzić w interakcje z interfejsem użytkownika, zapewnia bardziej „podstawowe” wątki, a brakuje wszystkich abstrakcji AsyncTask.

Chciałbym jednak mieć uruchomione połączenie z gniazdem. Czy należy to uruchomić w module obsługi, wątku, czy nawet w AsyncTask? Interakcja interfejsu użytkownika w ogóle nie jest konieczna. Czy ma to wpływ na wydajność, której używam?

Tymczasem dokumentacja została znacznie ulepszona.

Alx
źródło
9
„Handlery to wątki w tle” - wydaje się, że niektóre z najczęściej głosowanych odpowiedzi również zmierzają w tym kierunku. Ale to nieporozumienie. A Handlernie jest wątkiem i niczego nie wykonuje. To tylko sposób na bezpieczne przekazywanie wiadomości z jednego wątku do kolejki komunikatów innego wątku . Tak więc normalnie (przynajmniej) muszą zostać utworzone dwa wątki, które mogą następnie używać modułu obsługi, ale moduł obsługi nie może samodzielnie wykonać niczego.
JimmyB,

Odpowiedzi:

57

W samouczku na temat przetwarzania w tle w systemie Android za pomocą programów obsługi, AsyncTask i Loaders na stronie Vogella:

HandlerKlasy mogą być wykorzystane do rejestracji do wątku i zapewnia prosty kanał do przesyłania danych do tego wątku.

AsyncTaskKlasy obudowuje stworzenie procesu tła i synchronizacji z głównym gwintem. Obsługuje również raportowanie postępu uruchomionych zadań.

A Threadjest w zasadzie podstawowym elementem wielowątkowości, z którego programista może korzystać z następującą wadą:

Jeśli używasz wątków Java, musisz spełnić następujące wymagania we własnym kodzie:

  • Synchronizacja z głównym wątkiem, jeśli prześlesz wyniki z powrotem do interfejsu użytkownika
  • Brak wartości domyślnej dla anulowania wątku
  • Brak domyślnej puli wątków
  • Brak domyślnej obsługi zmian konfiguracji w Androidzie

A jeśli chodzi o AsyncTask, jak to ujęła Dokumentacja programisty Androida :

AsyncTaskumożliwia prawidłowe i łatwe korzystanie z wątku interfejsu użytkownika. Ta klasa umożliwia wykonywanie operacji w tle i publikowanie wyników w wątku interfejsu użytkownika bez konieczności manipulowania wątkami i / lub programami obsługi.

AsyncTaskjest zaprojektowany jako wokół klasy pomocnika Threadi Handler i nie stanowią rodzajowe ramy wątków. AsyncTasks najlepiej powinien być używany do krótkich operacji (najwyżej kilka sekund). Jeśli chcesz, aby wątki działały przez długi czas, zdecydowanie zalecamy korzystanie z różnych interfejsów API dostarczanych przez pakiet java.util.concurrent, takich jak Executor, ThreadPoolExecutor i FutureTask.

Aktualizacja maj 2015: Znalazłem doskonałą serię wykładów na ten temat.

To jest wyszukiwarka Google: Douglas Schmidt wykład android współbieżność i synchronizacja

To jest film z pierwszego wykładu na YouTube

Wszystko to jest częścią CS 282 (2013): Programowanie systemów dla Androida z Uniwersytetu Vanderbilt . Oto lista odtwarzania YouTube

Douglas Schmidt wydaje się być doskonałym wykładowcą

Ważne: jeśli jesteś w punkcie, w którym rozważasz AsyncTaskrozwiązanie problemów z wątkami, powinieneś najpierw sprawdzić, czyReactiveX/RxAndroid jest bardziej odpowiedni wzorzec programowania. Bardzo dobrym źródłem do uzyskania przeglądu jest Nauka RxJava 2 dla Androida na przykład .

Daniel F.
źródło
4
W tej serii wykładów ten link zabierze Cię do kilku przykładów wątków: youtu.be/4Vue_KuXfCk?t=19m24s
Aggressor
353

Jeśli spojrzymy na kod źródłowy, zobaczymy AsyncTaski Handlerjest napisany wyłącznie w Javie. (Są jednak wyjątki. Ale to nie jest ważny punkt)

Więc nie ma magii w AsyncTasklub Handler. Zajęcia te ułatwiają nam życie jako programista.

Na przykład: jeśli program A wywołuje metodę A (), metoda A () może działać w innym wątku z programem A. Możemy łatwo zweryfikować, wykonując następujący kod:

Thread t = Thread.currentThread();    
int id = t.getId();

Dlaczego powinniśmy używać nowego wątku do niektórych zadań? Możesz go znaleźć w Google. Wiele powodów, np .: ciężkie podnoszenie, długotrwałe prace.

Więc, jakie są różnice między Thread, AsyncTaski Handler?

AsyncTaski Handlersą napisane w Javie (wewnętrznie używają a Thread), więc wszystko, co możemy zrobić z, Handlerlub też AsyncTaskmożemy osiągnąć, używając Threadteż.

Co może Handleri AsyncTasknaprawdę może pomóc?

Najbardziej oczywistym powodem jest komunikacja między wątkiem wywołującym a wątkiem roboczym. ( Wątek dzwoniącego : Wątek, który wywołuje Wątek roboczy w celu wykonania niektórych zadań. Wątek dzwoniącego niekoniecznie musi być wątkiem interfejsu użytkownika). Oczywiście możemy komunikować się między dwoma wątkami na inne sposoby, ale istnieje wiele wad (i niebezpieczeństw) ze względu na bezpieczeństwo wątków.

Dlatego powinniśmy użyć Handleri AsyncTask. Klasy te wykonują za nas większość pracy, musimy tylko wiedzieć, które metody zastąpić.

Różnica między Handleri AsyncTaskto: Użyj, AsyncTaskgdy wątek dzwoniącego jest wątkiem interfejsu użytkownika . Tak mówi dokument Android:

AsyncTask umożliwia prawidłowe i łatwe korzystanie z wątku interfejsu użytkownika. Ta klasa umożliwia wykonywanie operacji w tle i publikowanie wyników w wątku interfejsu użytkownika bez konieczności manipulowania wątkami i / lub programami obsługi

Chcę podkreślić dwie kwestie:

1) Łatwe korzystanie z wątku interfejsu użytkownika (więc użyj, gdy wątkiem wywołującym jest wątek interfejsu użytkownika).

2) Nie trzeba manipulować programami obsługi. (oznacza: Możesz użyć modułu obsługi zamiast AsyncTask, ale AsyncTask jest łatwiejszą opcją).

W tym poście jest wiele rzeczy, których jeszcze nie powiedziałem, na przykład: co to jest wątek interfejsu użytkownika lub dlaczego jest to łatwiejsze. Musisz znać metody każdej klasy i korzystać z niej, w pełni zrozumiesz powód.

@: kiedy czytasz dokument Androida, zobaczysz:

Moduł obsługi umożliwia wysyłanie i przetwarzanie obiektów Message i Runnable powiązanych z MessageQueue wątku

Ten opis może początkowo wydawać się dziwny. Musimy tylko zrozumieć, że każdy wątek ma każdą kolejkę wiadomości (jak lista rzeczy do zrobienia), a wątek weźmie każdą wiadomość i zrobi to, dopóki kolejka wiadomości nie będzie pusta (tak jak kończymy naszą pracę i kładziemy się spać). Tak więc, gdy się Handlerkomunikuje, po prostu wysyła wiadomość do wątku dzwoniącego i będzie czekał na przetworzenie.

Skomplikowane? Pamiętaj tylko, że Handlermoże bezpiecznie komunikować się z wątkiem dzwoniącego.

hqt
źródło
4
tak naprawdę asynctask opiera się również na module handlera i futuretask, patrz
Sumit
AsyncTask to zasadniczo klasa pomocnicza zbudowana na bazie Handler and Thread. developer.android.com/reference/android/os/AsyncTask.html . Spójrz na dokument „AsyncTask został zaprojektowany jako klasa pomocnicza wokół Thread and Handler”. AsyncTask jest wydany w API3, podczas gdy moduł obsługi istnieje od API1.
hjchin
52

Po dogłębnym spojrzeniu jest to proste.

AsyncTask:

Jest to prosty sposób na użycie wątku bez wiedzy na temat modelu wątku Java . AsyncTaskdaje różne wywołania zwrotne odpowiednio do wątku roboczego i głównego wątku.

Użyj do małych operacji oczekiwania, takich jak:

  1. Pobieranie niektórych danych z usług internetowych i wyświetlanie w układzie.
  2. Zapytanie do bazy danych.
  3. Kiedy zdasz sobie sprawę, że uruchomiona operacja nigdy nie będzie nigdy zagnieżdżona.

Handler:

Gdy instalujemy aplikację w systemie Android, tworzy ona wątek dla tej aplikacji o nazwie MAIN UI Thread. Wszystkie działania przebiegają w tym wątku. Zgodnie z regułą modelu pojedynczego wątku Androida nie możemy uzyskać dostępu do elementów interfejsu użytkownika (bitmapy, widoku tekstu itp.) Bezpośrednio dla innego wątku zdefiniowanego w ramach tego działania.

Moduł obsługi umożliwia komunikację z wątkiem interfejsu użytkownika z innych wątków w tle. Jest to przydatne w Androidzie, ponieważ Android nie pozwala innym wątkom komunikować się bezpośrednio z wątkiem interfejsu użytkownika. Program obsługi może wysyłać i przetwarzać obiekty Message i Runnable powiązane z MessageQueue wątku. Każda instancja modułu obsługi jest powiązana z jednym wątkiem i kolejką komunikatów tego wątku. Gdy tworzony jest nowy moduł obsługi, jest on powiązany z kolejką wątków / komunikatów tworzącego go wątku.

To najlepiej pasuje do:

  1. Pozwala na kolejkowanie wiadomości.
  2. Planowanie wiadomości.

Thread:

Czas porozmawiać o wątku.

Wątek jest rodzicem zarówno AsyncTaski Handler. Oba używają wątku wewnętrznie, co oznacza, że możesz również stworzyć swój własny model wątku, jak AsyncTaski Handler, ale to wymaga dobrej znajomości implementacji wielowątkowości Javy .

Lavekush Agrawal
źródło
1
Interfejs API AsyncTask jest w rzeczywistości napisany przy użyciu kontraktów futures, handlerów i wykonawców. Zobacz kod źródłowy: grepcode.com/file_/repository.grepcode.com/java/ext/…
IgorGanapolsky 10.10.16
22

An AsyncTasksłuży do wykonywania obliczeń w tle i publikowania wyników w wątku interfejsu użytkownika (z opcjonalnymi aktualizacjami postępu). Ponieważ nie interesujesz się interfejsem użytkownika, a Handlerlub Threadwydaje się bardziej odpowiednie.

Można tarło tło Threadi przekazać wiadomości z powrotem do głównego wątku za pomocą Handler„s postmetody.

Eugene S.
źródło
9

Wątek

Android obsługuje standardowe wątki Java . Możesz użyć standardowych wątków i narzędzi z pakietu „ java.util.concurrent”, aby umieścić działania w tle. Jedynym ograniczeniem jest to, że nie można bezpośrednio zaktualizować interfejsu użytkownika z procesu działającego w tle.

Jeśli musisz zaktualizować interfejs użytkownika z zadania w tle, musisz użyć niektórych klas specyficznych dla Androida. Możesz użyć klasy „ android.os.Handler” do tego lub klasy „ AsyncTask

Treser

Klasa „ Handler” może aktualizować interfejs użytkownika. Uchwyt zapewnia metody odbierania komunikatów i uruchamiania. Aby użyć modułu obsługi, należy go podklasować i zastąpić w handleMessage()celu przetwarzania komunikatów. Aby przetworzyć Runable, możesz użyć metody post();Potrzebujesz tylko jednego wystąpienia procedury obsługi w swoim działaniu.

Wątek może publikować wiadomości za pomocą metody sendMessage(Message msg)lub sendEmptyMessage.

AsyncTask

Jeśli masz coś, Activityco wymaga pobrania treści lub wykonania operacji, które można wykonać w tle, AsyncTaskmożesz zachować responsywny interfejs użytkownika i opublikować postępy w zakresie tych operacji dla użytkownika.

Aby uzyskać więcej informacji, możesz przejrzeć te linki.

http://mobisys.in/blog/2012/01/android-threads-handlers-and-asynctask-tutorial/

http://www.slideshare.net/HoangNgoBuu/android-thread-handler-and-asynctask

Harshal Benake
źródło
6

Thread:

Możesz użyć nowego Threaddo długotrwałych zadań w tle bez wpływu na wątek interfejsu użytkownika. W wątku Java nie można aktualizować wątku interfejsu użytkownika.

Ponieważ normalny wątek nie jest zbyt przydatny w architekturze Androida, wprowadzono klasy pomocnicze dla wątków.

Odpowiedzi na pytania można znaleźć na stronie dokumentacji wydajności wątków .

Handler :

A Handlerumożliwia wysyłanie i przetwarzanie wiadomości i Runnableobiektów powiązanych z wątkiem MessageQueue. Każde Handlerwystąpienie jest powiązane z jednym wątkiem i kolejką komunikatów tego wątku.

Istnieją dwa główne zastosowania Handler:

  1. Aby zaplanować komunikaty i programy uruchamiające, które będą wykonywane w przyszłości;

  2. Aby kolejkować akcję, która ma być wykonana w innym wątku niż własny.

AsyncTask :

AsyncTaskumożliwia prawidłowe i łatwe korzystanie z wątku interfejsu użytkownika. Ta klasa umożliwia wykonywanie operacji w tle i publikowanie wyników w wątku interfejsu użytkownika bez konieczności manipulowania wątkami i / lub programami obsługi.

Wady:

  1. Domyślnie aplikacja wypycha wszystkie utworzone AsyncTaskobiekty do jednego wątku. Dlatego są one wykonywane szeregowo i - podobnie jak w przypadku głównego wątku - szczególnie długi pakiet roboczy może blokować kolejkę. Z tego powodu użyj AsyncTask do obsługi elementów roboczych o czasie trwania krótszym niż 5ms .

  2. AsyncTaskobiekty są również najczęstszymi sprawcami niejawnych odniesień. AsyncTaskobiekty stanowią również ryzyko związane z wyraźnymi odniesieniami.

HandlerThread :

Może być potrzebne bardziej tradycyjne podejście do wykonywania bloku pracy w wątku długo działającym (w przeciwieństwie do AsyncTask, który powinien być używany przy obciążeniu 5 ms ) oraz pewna umiejętność ręcznego zarządzania tym przepływem pracy. Wątek programu obsługi jest w rzeczywistości długo działającym wątkiem, który pobiera pracę z kolejki i działa na niej.

ThreadPoolExecutor :

Ta klasa zarządza tworzeniem grupy wątków, ustala ich priorytety i zarządza rozkładem pracy między tymi wątkami. Gdy obciążenie wzrasta lub maleje, klasa obraca się w górę lub niszczy więcej wątków, aby dostosować się do obciążenia.

Jeśli obciążenie jest większe, a pojedyncze HandlerThreadnie wystarcza, możesz przejść dalejThreadPoolExecutor

Chciałbym jednak mieć uruchomione połączenie z gniazdem. Czy należy to uruchomić w module obsługi, wątku, a nawet AsyncTask? Interakcja interfejsu użytkownika w ogóle nie jest konieczna. Czy ma to wpływ na wydajność, której używam?

Ponieważ interakcja w interfejsie użytkownika nie jest wymagana, nie możesz iść AsyncTask. Normalne wątki nie są zbyt przydatne i dlatego HandlerThreadsą najlepszą opcją. Ponieważ musisz zachować połączenie z gniazdem, moduł obsługi głównego wątku nie jest w ogóle przydatny. Utwórz a HandlerThreadi zdobądź Handlerz looper'a HandlerThread.

 HandlerThread handlerThread = new HandlerThread("SocketOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());
 requestHandler.post(myRunnable); // where myRunnable is your Runnable object. 

Jeśli chcesz komunikować się z powrotem w wątku interfejsu użytkownika, możesz użyć jeszcze jednego modułu obsługi do przetworzenia odpowiedzi.

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            //txtView.setText((String) msg.obj);
            Toast.makeText(MainActivity.this,
                    "Foreground task is completed:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

w twoim Runnable, możesz dodać

responseHandler.sendMessage(msg);

Więcej informacji na temat implementacji można znaleźć tutaj:

Android: Toast w wątku

Ravindra babu
źródło
5

Moim zdaniem wątki nie są najskuteczniejszym sposobem wykonywania połączeń przez gniazda, ale zapewniają największą funkcjonalność pod względem uruchamiania wątków. Mówię to, ponieważ z doświadczenia, uruchamianie wątków przez długi czas powoduje, że urządzenia są bardzo gorące i wymagają dużej ilości zasobów. Nawet zwykły while(true)telefon nagrzeje telefon w kilka minut. Jeśli powiesz, że interakcja z interfejsem użytkownika nie jest ważna, być może AsyncTaskjest dobra, ponieważ są one przeznaczone do długotrwałych procesów. To tylko moja opinia na ten temat.

AKTUALIZACJA

Proszę zignoruj ​​moją powyższą odpowiedź! Odpowiedziałem na to pytanie w 2011 roku, kiedy byłem znacznie mniej doświadczony w Androidzie niż teraz. Moja odpowiedź powyżej jest myląca i uważana za błędną. Zostawiam to, ponieważ wiele osób skomentowało to poniżej, poprawiając mnie i nauczyłem się mojej lekcji.

Istnieją znacznie lepsze inne odpowiedzi w tym wątku, ale przynajmniej dam mi bardziej odpowiednią odpowiedź. Nie ma nic złego w korzystaniu ze zwykłej Java Thread; jednak powinieneś bardzo uważać na to, jak go zaimplementować, ponieważ jego nieprawidłowe wykonanie może być bardzo obciążające procesor (najbardziej zauważalnym objawem może być nagrzewanie się urządzenia). AsyncTasks są dość idealne do większości zadań, które chcesz uruchomić w tle (typowe przykłady to dyskowe operacje we / wy, połączenia sieciowe i połączenia z bazą danych). Nie AsyncTasknależy go jednak używać do szczególnie długich procesów, które mogą wymagać kontynuacji po zamknięciu aplikacji lub przejściu urządzenia w tryb gotowości. Powiedziałbym, że w większości przypadków wszystko, co nie należy do wątku interfejsu użytkownika, można załatwić w AsyncTask.

Brian
źródło
dzięki, czy faktycznie jest powód, dla którego powinienem używać Wątków zamiast AsyncTasks? A może lepiej jest z niego skorzystać?
Alx,
9
@AeroDroid W twoim przykładzie: „prosta chwila (prawda)”, przypisz procesor tutaj, chyba że dodasz stan uśpienia w pętli. Dotyczy to każdej nieskończonej pętli. Jeśli chcesz zmniejszyć użycie procesora z powodu tego obciążenia, uśp wątek na kilka milisekund na końcu pętli.
Błąd 454
1
@ Błąd 454 - to ciekawe! Gdybyś musiał wybrać odpowiednią liczbę na czas snu, czy byłaby to między 40-80 milisekund?
Abhijit,
6
@Abhijit Z rzeczy, które zrobiłem w SDL, wystarczyło dodać 10 ms do pętli, aby spadać z 99% procesora do ~ 0 w stanach bezczynności.
Błąd 454
15
W rzeczywistości developer.android.com/reference/android/os/AsyncTask.html mówi: „Idealnie należy używać AsyncTasks do operacji KRÓTKICH”. Powinieneś także używać ich ostrożnie, ponieważ mogą zostać zwolnione przez system bez wykonywania!
type-a1pha
5

AsyncTaskjest przeznaczony do wykonywania w tle nie więcej niż kilku sekund operacji (niezalecane w przypadku megabajtów pobierania plików z serwera lub obliczeniowych zadań procesora, takich jak operacje na plikach we / wy). Jeśli chcesz wykonać długo działającą operację, zdecydowanie zaleca się używanie rodzimych wątków Java. Java oferuje różne klasy związane z wątkami, aby robić to, czego potrzebujesz. Służy Handlerdo aktualizowania wątku interfejsu użytkownika.

niebo
źródło
2
public class RequestHandler {

    public String sendPostRequest(String requestURL,
                                  HashMap<String, String> postDataParams) {

        URL url;

        StringBuilder sb = new StringBuilder();
        try {
            url = new URL(requestURL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(getPostDataString(postDataParams));

            writer.flush();
            writer.close();
            os.close();
            int responseCode = conn.getResponseCode();

            if (responseCode == HttpsURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                sb = new StringBuilder();
                String response;
                while ((response = br.readLine()) != null){
                    sb.append(response);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        return result.toString();
    }

}
krishna kulat
źródło
1

Pozwól, że spróbuję odpowiedzieć na pytanie tutaj z przykładem :) - MyImageSearch [Prosimy odnieść się tutaj do obrazu głównego ekranu aktywności - zawierającego tekst edycji / przycisk wyszukiwania / widok siatki]

MyImageSearch

Opis MyImageSearch - gdy użytkownik wprowadzi dane w polu edycji tekstu i kliknie przycisk wyszukiwania, przeszukamy obrazy w Internecie za pośrednictwem usług internetowych świadczonych przez flickr (wystarczy się tam zarejestrować, aby uzyskać klucz / tajny token) - do wyszukiwania wysyłamy żądanie HTTP i GET JSON Data w odpowiedzi, zawierające adres URL poszczególnych obrazów, które następnie wykorzystamy do załadowania widoku siatki.

Moja implementacja - w głównym zadaniu zdefiniuję wewnętrzną klasę, która rozszerza AsyncTask o wysyłanie żądania HTTP w metodzie doInBackGround i pobiera odpowiedź JSON oraz aktualizuje moją lokalną ArrayList FlickrItems, której zamierzam użyć do aktualizacji mojego GridView poprzez FlickrAdapter (rozszerza BaseAdapter) i wywołuje adapter.notifyDataSetChanged () w onPostExecute () AsyncTask, aby ponownie załadować widok siatki. Zauważ, że tutaj żądanie HTTP jest wywołaniem blokującym, z powodu którego zrobiłem to za pomocą AsyncTask. I mogę buforować elementy w adapterze, aby zwiększyć wydajność lub zapisać je na karcie SD. Siatka, którą będę pompować w FlickrAdapter zawiera w mojej implementacji pasek postępu i widok obrazu. Poniżej znajdziesz kod dla mainActivity, którego użyłem.

Odpowiedz teraz na pytanie - kiedy już mamy dane JSON do pobierania poszczególnych obrazów, możemy zaimplementować logikę pobierania obrazów w tle za pośrednictwem programów obsługi, wątków lub AsyncTask. Powinniśmy tutaj zauważyć, że ponieważ moje pobrane obrazy muszą być wyświetlane w interfejsie użytkownika / wątku głównym, nie możemy po prostu używać wątków, ponieważ nie mają one dostępu do kontekstu. W FlickrAdapter wybory, które mogłem wymyślić:

  • Wybór 1: Utwórz LooperThread [rozszerza wątek] - i kontynuuj pobieranie zdjęć sekwencyjnie w jednym wątku, utrzymując ten wątek otwarty [looper.loop ()]
  • Wybór 2: skorzystaj z puli wątków i opublikuj plik uruchamialny za pośrednictwem myHandler, który zawiera odwołanie do mojego ImageView, ale ponieważ widoki w widoku siatki są przetwarzane ponownie, ponownie może pojawić się problem, gdy obraz o indeksie 4 jest wyświetlany przy indeksie 9 [pobieranie może poświęć więcej czasu]
  • Wybór 3 [Użyłem tego]: skorzystaj z puli wątków i wyślij wiadomość do myHandler, który zawiera dane związane z indeksem ImageView i samym ImageView, więc podczas wykonywania handleMessage () zaktualizujemy ImageView tylko wtedy, gdy currentIndex pasuje do indeksu obraz, który próbowaliśmy pobrać.
  • Wybór 4: skorzystaj z AsyncTask, aby pobrać obrazy w tle, ale tutaj nie będę miał dostępu do liczby wątków, które chcę w puli wątków i różni się w zależności od wersji Androida, ale w Choice 3 mogę podjąć świadomą decyzję wielkości puli wątków w zależności od używanej konfiguracji urządzenia.

Oto kod źródłowy:

public class MainActivity extends ActionBarActivity {

    GridView imageGridView;
    ArrayList<FlickrItem> items = new ArrayList<FlickrItem>();
    FlickrAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageGridView = (GridView) findViewById(R.id.gridView1);
        adapter = new FlickrAdapter(this, items);
        imageGridView.setAdapter(adapter);
    }

    // To avoid a memory leak on configuration change making it a inner class
    class FlickrDownloader extends AsyncTask<Void, Void, Void> {



        @Override
        protected Void doInBackground(Void... params) {
            FlickrGetter getter = new FlickrGetter();

            ArrayList<FlickrItem> newItems = getter.fetchItems();

            // clear the existing array
            items.clear();

            // add the new items to the array
            items.addAll(newItems);

            // is this correct ? - Wrong rebuilding the list view and should not be done in background
            //adapter.notifyDataSetChanged();

            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);

            adapter.notifyDataSetChanged();
        }

    }

    public void search(View view) {
        // get the flickr data
        FlickrDownloader downloader = new FlickrDownloader();
        downloader.execute();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

Mam nadzieję, że moja długa odpowiedź pomoże w zrozumieniu niektórych drobniejszych szczegółów.

akshaymani
źródło
Czy mogę poznać powód, dla którego moje wyjaśnienie na podstawie przykładu dla analogii zostało odrzucone, aby móc się z niego uczyć?
akshaymani
2
Przede wszystkim dziękuję za odpowiedź, mimo że ten temat jest nieco stary, podstawowe pojęcia pozostają aktualne. Na moje początkowe pytanie w ogóle nie ma odpowiedzi, podajesz przykład i wyjaśniasz, jak to działa, ale pytania dotyczą różnic między programem obsługi, zadaniem asynchronicznym i wątkiem.
Alx
@ 80leaves ok, rozumiem teraz, próbowałem wyjaśnić, w jaki sposób doszedłem do wniosku, że wybrałem jedną drogę nad drugą. Tak czy inaczej, chciałbym usłyszeć twoje / inne opinie na temat tego, czy to, co napisałem, jest poprawne lub czy można je dalej ulepszyć.
akshaymani
1

Zależy, który wybrać, zależy od wymagań

Handler jest najczęściej używany do przełączania z innego wątku na główny wątek, Handler jest podłączony do looper'a, na którym umieszcza swoje uruchamialne zadanie w kolejce. Więc jeśli jesteś już w innym wątku i przełączasz się na główny wątek, potrzebujesz uchwytu zamiast zadania asynchronicznego lub innego wątku

Jeśli moduł obsługi utworzony w wątku innym niż główny, który nie jest looperem, nie będzie dawał błędu, gdy uchwyt zostanie utworzony, wątek ten musi zostać zmieniony

AsyncTask służy do wykonywania kodu przez kilka sekund, który działa w wątku w tle i podaje wynik wątkowi głównemu ** * Ograniczenia AsyncTask 1. Zadanie Async nie jest powiązane z cyklem życia działania i działa dalej, nawet jeśli jego aktywność zostanie zniszczona, a moduł ładujący nie nie mają tego ograniczenia 2. Wszystkie zadania asynchroniczne mają ten sam wątek tła do wykonania, co również wpływa na wydajność aplikacji

Wątek jest również używany w aplikacji do pracy w tle, ale nie ma żadnego wywołania zwrotnego w głównym wątku. Jeśli wymaganie pasuje do niektórych wątków zamiast jednego wątku i które muszą dawać zadanie wiele razy, to wykonawca puli wątków jest lepszą opcją. Wymaganie ładowania obrazu z wielu adresów URL, takich jak glide.

mani
źródło
0

Wątek

Po uruchomieniu aplikacji tworzony jest proces wykonywania kodu. Aby efektywnie korzystać z zasobów obliczeniowych, w procesie można uruchamiać wątki, aby jednocześnie wykonywać wiele zadań. Wątki pozwalają więc tworzyć wydajne aplikacje dzięki wydajnemu wykorzystaniu procesora bez przestoju.

W Androidzie wszystkie komponenty są uruchamiane w jednym wątku głównym. Kolejki systemowe w systemie Android wykonują je kolejno w głównym wątku. Po wykonaniu długo działających zadań aplikacja przestaje odpowiadać.

Aby temu zapobiec, możesz tworzyć wątki robocze i uruchamiać zadania w tle lub zadania długo działające.

Treser

Ponieważ Android korzysta z modelu jednowątkowego, komponenty interfejsu użytkownika są tworzone jako bezpieczne dla wątków, co oznacza, że ​​tylko utworzony przez nich wątek powinien mieć do nich dostęp, co oznacza, że ​​komponent interfejsu powinien być aktualizowany tylko w głównym wątku. Ponieważ składnik interfejsu użytkownika działa w głównym wątku, zadania uruchamiane w wątkach roboczych nie mogą modyfikować składników interfejsu użytkownika. Tutaj pojawia się Handler. Program obsługi za pomocą Looper może łączyć się z nowym wątkiem lub istniejącym wątkiem i uruchamiać kod, który zawiera w połączonym wątku.

Program obsługi umożliwia komunikację między wątkami. Za pomocą modułu obsługi wątek w tle może wysyłać do niego wyniki, a moduł obsługi podłączony do głównego wątku może aktualizować komponenty interfejsu użytkownika w głównym wątku.

AsyncTask

AsyncTask dostarczany przez Androida wykorzystuje zarówno wątek, jak i moduł obsługi, aby ułatwić uruchamianie prostych zadań w tle i łatwą aktualizację wyników z wątku w tle do głównego.

Przykłady można znaleźć w wątkach Androida, module obsługi, asynctask i pulach wątków .

Arnav Rao
źródło
-1

Handler- jest środkiem komunikacji między wątkami. W Androidzie służy głównie do komunikowania się z głównym wątkiem poprzez tworzenie i wysyłanie wiadomości za pomocą modułu obsługi

AsyncTask- służy do wykonywania długo działających aplikacji w wątku w tle. Za pomocą n AsyncTaskmożesz wykonać operację w wątku w tle i uzyskać wynik w głównym wątku aplikacji.

Thread- jest procesem lekkim, aby osiągnąć współbieżność i maksymalne wykorzystanie procesora. W Androidzie możesz używać wątku do wykonywania czynności, które nie dotykają interfejsu użytkownika aplikacji

arjun
źródło