Timertask lub Handler

104

Powiedzmy, że chcę wykonywać jakąś czynność co 10 sekund i niekoniecznie muszę aktualizować widok.

Pytanie brzmi: czy lepiej (mam na myśli bardziej wydajne i efektywne) używać timera z timertask, jak tutaj:

final Handler handler = new Handler();

TimerTask timertask = new TimerTask() {
    @Override
    public void run() {
        handler.post(new Runnable() {
            public void run() {
               <some task>
            }
        });
    }
};
timer = new Timer();
timer.schedule(timertask, 0, 15000);
}

lub po prostu handler z opóźnieniem

final Handler handler = new Handler(); 
final Runnable r = new Runnable()
{
    public void run() 
    {
        <some task>
    }
};
handler.postDelayed(r, 15000);

Byłbym również wdzięczny, gdybyś mógł wyjaśnić, kiedy zastosować które podejście i dlaczego jedno z nich jest bardziej wydajne niż inne (jeśli rzeczywiście jest).

keysersoze
źródło
2
Przeczytałem wiele postów o nieregularnym działaniu TimerTasks. Moja rada to omijanie ich i stosowanie podejścia obsługi / postDelayed.
Sound Conception
1
Wolałbym metodę Handler-postDelay - masz większą kontrolę i planujesz to od wewnątrz
mihail
1
Oto świetne źródło dla Timer vs. Handler
CodyF,
TimerTask jest zadaniem w tle, więc nie możesz zaktualizować interfejsu użytkownika. Tylko mówię ...
Yousha Aleayoub
1
pracował dla mnie .. dzięki
jyotsna

Odpowiedzi:

97

Handlerjest lepszy niż TimerTask.

Java TimerTaski Android Handlerumożliwiają planowanie opóźnionych i powtarzających się zadań w wątkach w tle. Jednakże, w literaturze zdecydowanie zaleca stosowanie Handlerna TimerTaskAndroid (patrz o , o , o , o , o , a tutaj ).

Niektóre z zgłoszonych problemów z TimerTask obejmują:

  • Nie można zaktualizować wątku interfejsu użytkownika
  • Wycieki pamięci
  • Zawodne (nie zawsze działa)
  • Długotrwałe zadania mogą kolidować z następnym zaplanowanym zdarzeniem

Przykład

Najlepszym źródłem wszelkiego rodzaju przykładów Androida, które widziałem, jest Codepath . Oto Handlerprzykład powtarzającego się zadania.

// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
    @Override
    public void run() {
      // Do something here on the main thread
      Log.d("Handlers", "Called on main thread");
      // Repeat this the same runnable code block again another 2 seconds
      handler.postDelayed(runnableCode, 2000);
    }
};
// Start the initial runnable task by posting through the handler
handler.post(runnableCode);

Związane z

Suragch
źródło
6
@Reek Nie, GC powinno się tym zająć. Ale musisz zadbać o plik runnable wysłany do opóźnionego wykonania. W powyższym przykładzie użytym elementem wykonawczym jest instancja klasy wewnętrznej, więc zawiera niejawne odwołanie do klasy zawierającej (która może być działaniem). Runnable pozostanie w kolejce komunikatów pętli obsługi powiązanej z programem obsługi aż do następnego czasu wykonania, który może nastąpić po tym, jak kontekst jest nieprawidłowy i może przeciekać zawierającą instancję klasy. Możesz usunąć takie odniesienia, używając mHandler.removeCallbacks(runnableCode)w odpowiednim czasie (np. W onStop()przypadku działania).
bitbybit
7
Najlepszy sposób na zaprezentowanie referencji !!! (patrz tutaj, tutaj, tutaj, tutaj, tutaj i tutaj).
iRavi iVooda
a co, jeśli chcę użyć tego w ViewModel? nie jest sprzeczne z ideałem nie posiadania tam androidów?
desgraci
@desgraci, nie korzystałem z ViewModel, ale z dokumentacji widzę tylko, że mówi, że ViewModel nie powinien uzyskiwać dostępu do hierarchii widoku ani zawierać odniesienia do działania lub fragmentu. Nie widzę w ogóle niczego, co zabraniałoby posiadania „rzeczy na Androida”.
Suragch
Na dzień dzisiejszy te odniesienia są dla mnie nieaktualne i niewystarczająco pouczające, aby można je było wziąć pod uwagę. Te 4 wymienione wady są prawdziwe tylko wtedy, gdy źle zaprogramujesz swój kod. TimerTasks są nadal bardzo dobrym wyborem, jeśli chcesz okresowo uruchamiać coś w tle i ostatecznie uruchamiać coś na UIThread, jeśli zachodzi jakiś warunek.
David
18

Korzystanie z funkcji Timer ma kilka wad

Tworzy tylko jeden wątek do wykonywania zadań, a jeśli zadanie trwa zbyt długo, inne zadania cierpią. Nie obsługuje wyjątków generowanych przez zadania, a wątek po prostu się kończy, co wpływa na inne zaplanowane zadania i nigdy nie są uruchamiane

Skopiowano z:

TimerTask vs Thread.sleep vs Handler postDelayed - najdokładniejsze wywołanie funkcji co N milisekund?

Praveena
źródło
6
a co z jednorazowym zadaniem? wygląda na to, że Timer jest lepszy do tego, ponieważ nie masz narzutu kolejki wiadomości?
Michael
2
Chyba nigdy się nie dowiemy
Denny
7

Wersja zaakceptowanej odpowiedzi Kotlin:

val handler = Handler()

val runnableCode = object : Runnable {
    override fun run() {
       Log.d("Handlers", "Called on main thread")
       handler.postDelayed(this, 2000)
    }
}

handler.post(runnableCode)
sma6871
źródło