ok, ponieważ nie zostało to jeszcze wyjaśnione, istnieją 3 proste sposoby radzenia sobie z tym. Poniżej znajduje się przykład pokazujący wszystkie 3, a na dole przykład pokazujący tylko metodę, która moim zdaniem jest lepsza. Pamiętaj także o wyczyszczeniu zadań w trybie onPause, w razie potrzeby zapisując stan.
import java.util.Timer;import java.util.TimerTask;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.os.Handler.Callback;import android.view.View;import android.widget.Button;import android.widget.TextView;publicclass main extendsActivity{TextView text, text2, text3;long starttime =0;//this posts a message to the main thread from our timertask//and updates the textfieldfinalHandler h =newHandler(newCallback(){@Overridepublicboolean handleMessage(Message msg){long millis =System.currentTimeMillis()- starttime;int seconds =(int)(millis /1000);int minutes = seconds /60;
seconds = seconds %60;
text.setText(String.format("%d:%02d", minutes, seconds));returnfalse;}});//runs without timer be reposting selfHandler h2 =newHandler();Runnable run =newRunnable(){@Overridepublicvoid run(){long millis =System.currentTimeMillis()- starttime;int seconds =(int)(millis /1000);int minutes = seconds /60;
seconds = seconds %60;
text3.setText(String.format("%d:%02d", minutes, seconds));
h2.postDelayed(this,500);}};//tells handler to send a messageclass firstTask extendsTimerTask{@Overridepublicvoid run(){
h.sendEmptyMessage(0);}};//tells activity to run on ui threadclass secondTask extendsTimerTask{@Overridepublicvoid run(){
main.this.runOnUiThread(newRunnable(){@Overridepublicvoid run(){long millis =System.currentTimeMillis()- starttime;int seconds =(int)(millis /1000);int minutes = seconds /60;
seconds = seconds %60;
text2.setText(String.format("%d:%02d", minutes, seconds));}});}};Timer timer =newTimer();@Overridepublicvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);
setContentView(R.layout.main);
text =(TextView)findViewById(R.id.text);
text2 =(TextView)findViewById(R.id.text2);
text3 =(TextView)findViewById(R.id.text3);Button b =(Button)findViewById(R.id.button);
b.setText("start");
b.setOnClickListener(newView.OnClickListener(){@Overridepublicvoid onClick(View v){Button b =(Button)v;if(b.getText().equals("stop")){
timer.cancel();
timer.purge();
h2.removeCallbacks(run);
b.setText("start");}else{
starttime =System.currentTimeMillis();
timer =newTimer();
timer.schedule(new firstTask(),0,500);
timer.schedule(new secondTask(),0,500);
h2.postDelayed(run,0);
b.setText("stop");}}});}@Overridepublicvoid onPause(){super.onPause();
timer.cancel();
timer.purge();
h2.removeCallbacks(run);Button b =(Button)findViewById(R.id.button);
b.setText("start");}}
najważniejszą rzeczą do zapamiętania jest to, że interfejs użytkownika można modyfikować tylko z głównego wątku interfejsu użytkownika, więc użyj programu obsługi lub activity.runOnUIThread (Runnable r);
Oto, co uważam za preferowaną metodę.
import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.widget.Button;import android.widget.TextView;publicclassTestActivityextendsActivity{TextView timerTextView;long startTime =0;//runs without a timer by reposting this handler at the end of the runnableHandler timerHandler =newHandler();Runnable timerRunnable =newRunnable(){@Overridepublicvoid run(){long millis =System.currentTimeMillis()- startTime;int seconds =(int)(millis /1000);int minutes = seconds /60;
seconds = seconds %60;
timerTextView.setText(String.format("%d:%02d", minutes, seconds));
timerHandler.postDelayed(this,500);}};@Overridepublicvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
timerTextView =(TextView) findViewById(R.id.timerTextView);Button b =(Button) findViewById(R.id.button);
b.setText("start");
b.setOnClickListener(newView.OnClickListener(){@Overridepublicvoid onClick(View v){Button b =(Button) v;if(b.getText().equals("stop")){
timerHandler.removeCallbacks(timerRunnable);
b.setText("start");}else{
startTime =System.currentTimeMillis();
timerHandler.postDelayed(timerRunnable,0);
b.setText("stop");}}});}@Overridepublicvoid onPause(){super.onPause();
timerHandler.removeCallbacks(timerRunnable);Button b =(Button)findViewById(R.id.button);
b.setText("start");}}
@ Dave.B, dzięki za świetny przykład. Czy są jakieś zalety / wady korzystania z jednej metody w porównaniu z innymi, które przedstawiłeś?
Gautam
2
@Gautam Uważam, że wszystkie powyższe metody działają mniej więcej tak samo. Osobiście wolę opisaną powyżej metodę obsługi z uruchomieniem Runnable i h2 Handler, ponieważ jest to metoda zalecana przez witrynę dla programistów Androida i moim zdaniem również najbardziej elegancka.
Dave.B
6
Byłoby miło oddzielić preferowaną metodę od reszty kodu. Tak jak możesz mieć jeden przykład pokazujący preferowany sposób, a drugi pokazujący alternatywy. Posiadanie wszystkich trzech metod razem sprawia, że trudniej jest zrozumieć, co się dzieje (szczególnie dla nowicjuszy z Androidem, takiego jak ja). Prawdopodobnie jednak zbyt wiele pyta :)
Jesse Aldridge
4
@JesseAldridge Dobry pomysł. Poszedłem dalej i dodałem kod tylko za pomocą preferowanej metody.
Dave.B
1
@bluesm Szczerze mówiąc, po prostu o tym nie myślałem, ale tak, to by działało dobrze.
Dave.B
84
To jest proste! Tworzysz nowy licznik czasu.
Timer timer =newTimer();
Następnie rozszerzasz zadanie timera
classUpdateBallTaskextendsTimerTask{Ball myBall;publicvoid run(){//calculate the new position of myBall}}
A następnie dodaj nowe zadanie do Timera z pewnym interwałem aktualizacji
Oświadczenie: To nie jest idealne rozwiązanie. Jest to rozwiązanie wykorzystujące klasę Timer (zgodnie z zapytaniem OP). W zestawie Android SDK zaleca się użycie klasy Handler (w zaakceptowanej odpowiedzi znajduje się przykład).
jeśli przeczytasz powyższy post, zobaczysz, dlaczego nie jest to idealne rozwiązanie
Dave.B
2
Oczywiście. OP chciał to zrobić za pomocą TimerTask, którego nie polecam do użycia w grze.
fikcja
3
Co? OP nie określił, jak tego chcą. Odsyłali do artykułu, który korzystał z TimerTask, ale nie prosili, aby zrobić to w ten sposób.
ToolmakerSteve
1
Pomógł bardzo, Dzięki @fiction
Naveed Ahmad
1
świetna odpowiedź prosta do naśladowania.
JMASTER B,
64
Jeśli musisz także uruchomić swój kod w wątku interfejsu użytkownika (a nie w wątku licznika czasu), zajrzyj na blog: http://steve.odyfamily.com/?p=12
publicclass myActivity extendsActivity{privateTimer myTimer;/** Called when the activity is first created. */@Overridepublicvoid onCreate(Bundle icicle){super.onCreate(icicle);
setContentView(R.layout.main);
myTimer =newTimer();
myTimer.schedule(newTimerTask(){@Overridepublicvoid run(){TimerMethod();}},0,1000);}privatevoidTimerMethod(){//This method is called directly by the timer//and runs in the same thread as the timer.//We call the method that will work with the UI//through the runOnUiThread method.this.runOnUiThread(Timer_Tick);}privateRunnableTimer_Tick=newRunnable(){publicvoid run(){//This method runs in the same thread as the UI. //Do something to the UI thread here}};}
Dla kompletności możesz wspomnieć, co zrobić, aby zatrzymać stoper, a może zrestartować go. (Potrzebne informacje znalazłem tutaj: stackoverflow.com/questions/11550561/... )
RenniePet
3
Czy jest jakiś powód, dla którego nie można po prostu wywołać runOnUIThread bezpośrednio z metody uruchamiania TimerTask? Wydaje się, że działa dobrze i usuwa kolejny poziom zagnieżdżania.
RichieHH,
Jasne, to tylko metoda dydaktyczna do zrozumienia wszystkich kroków. Sugeruję, aby ten standard miał czytelny kod.
Meir Gerenstadt
41
Jeśli po prostu chcesz zaplanować odliczanie do czasu w przyszłości z regularnymi powiadomieniami o odstępach czasu po drodze, możesz użyć klasy CountDownTimer, która jest dostępna od poziomu API 1.
CountDownTimer ma sens tylko wtedy, gdy wiesz, że chcesz, aby zniknął po kilku egzekucjach. To nie jest typowe ani szczególnie elastyczne podejście. Częstszym jest licznik czasu, który powtarza się na zawsze (który anulujesz, gdy nie jest już potrzebny) lub moduł obsługi, który uruchamia się raz, a następnie uruchamia się ponownie, jeśli będzie ponownie potrzebny. Zobacz inne odpowiedzi.
ToolmakerSteve
1
Masz całkowitą rację. Od nazwy klasy zapewnia jednorazowe odliczanie timera aż do końca i oczywiście używa Handlera w swojej implementacji.
Ahmed Hegazy,
Jak również wyświetlać milisekundy? W formacie SS:MiMi? Dzięki
Ruchir Baronia,
26
Oto prosty kod dla timera:
Timer timer =newTimer();TimerTask t =newTimerTask(){@Overridepublicvoid run(){System.out.println("1");}};
timer.scheduleAtFixedRate(t,1000,1000);
timerSubscribe =Observable.interval(1,TimeUnit.SECONDS).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(newAction1<Long>(){@Overridepublicvoid call(Long aLong){//TODO do your stuff}});
Urzędnik zaproponował sposobem jest użycie ScheduledThreadPoolExecutor który jest bardziej skuteczny i dysponuje bogatym w które można dodatkowo komendy rozkład jazdy po danym opóźnienie lub okresowo wykonywać. Ponadto zapewnia dodatkową elastyczność i możliwości ThreadPoolExecutor.
Możesz teraz użyć, futureaby anulować zadanie lub sprawdzić, czy zostało wykonane, na przykład:
future.isDone();
Mam nadzieję, że okaże się to przydatne do tworzenia zadań w Androidzie.
Kompletny przykład:
ScheduledExecutorService scheduler =Executors.newScheduledThreadPool(1);Future<?> sampleFutureTimer = scheduler.schedule(newRunnable(),120,TimeUnit.SECONDS);if(sampleFutureTimer.isDone()){// Do something which will save world.}
Dziwi mnie, że nie ma odpowiedzi, która wspomniałaby o rozwiązaniu z RxJava2 . To jest naprawdę proste i zapewnia łatwy sposób na ustawienie timera w Androidzie.
Najpierw musisz skonfigurować zależność Gradle, jeśli jeszcze tego nie zrobiłeś:
Ponieważ mamy tylko proste, BEZ POWTARZANIA ZADANIE , możemy użyć Completableobiektu:
Completable.timer(2,TimeUnit.SECONDS,Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(()->{// Timer finished, do something...});
W przypadku POWTARZANIA ZADANIA można użyć Observablew podobny sposób:
Observable.interval(2,TimeUnit.SECONDS,Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(tick ->{// called every 2 seconds, do something...}, throwable ->{// handle error});
Schedulers.computation() zapewnia, że nasz zegar działa na wątku w tle i .observeOn(AndroidSchedulers.mainThread()) oznacza, że kod uruchamiany po zakończeniu timera zostanie wykonany w głównym wątku.
Aby uniknąć niechcianych wycieków pamięci, należy anulować subskrypcję, gdy Aktywność / Fragment zostanie zniszczona.
jak je anulować? tzn. gdy użytkownik naciśnie przycisk [STOP] na interfejsie użytkownika, a element Completable zostanie anulowany przed wykonaniem.
Ktoś gdzieś
@SomeoneSomewhere Wystarczy zapisać Subscriptionzwrócony przez .subscribe()metodę w zmiennej, a następnie wywołać, subscription.unsubscribe()gdy chcesz zatrzymać stoper.
Micer
2
Jest prostszym rozwiązaniem, działa dobrze w mojej aplikacji.
publicclassMyActivityextendsAcitivity{TextView myTextView;boolean someCondition=true;@Overrideprotectedvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
myTextView =(TextView) findViewById(R.id.refreshing_field);//starting our task which update textview every 1000 msnewRefreshTask().execute();}//class which updates our textview every secondclassRefreshTaskextendsAsyncTask{@Overrideprotectedvoid onProgressUpdate(Object... values){super.onProgressUpdate(values);String text =String.valueOf(System.currentTimeMillis());
myTextView.setText(text);}@OverrideprotectedObject doInBackground(Object...params){while(someCondition){try{//sleep for 1s in background...Thread.sleep(1000);//and update textview in ui thread
publishProgress();}catch(InterruptedException e){
e.printStackTrace();};returnnull;}}}
Chcesz, aby aktualizacje interfejsu użytkownika miały miejsce w już istniejącym wątku interfejsu użytkownika.
Najlepszym sposobem jest użycie modułu obsługi, który korzysta z funkcji postDelayed, aby uruchomić Runnable po opóźnieniu (każde uruchomienie planuje następne); wyczyść połączenie zwrotne za pomocą removeCallbacks.
Patrzysz już we właściwe miejsce, więc spójrz na to jeszcze raz, być może wyjaśnij, dlaczego ta próbka kodu nie jest tym, czego chcesz. (Zobacz także identyczny artykuł na temat aktualizacji interfejsu użytkownika z timera ).
Umieść następujący kod w swojej aktywności, a metoda tick () będzie wywoływana co sekundę w wątku interfejsu użytkownika, gdy twoja aktywność będzie w stanie „wznowionym”. Oczywiście możesz zmienić metodę tick (), aby robić to, co chcesz, lub być nazywanym mniej lub bardziej często.
Dla zainteresowanych kod „_h0 = _handler” jest niezbędny, aby uniknąć jednoczesnego działania dwóch timerów, jeśli aktywność zostanie wstrzymana i wznowiona w okresie tykania.
Musisz utworzyć wątek do obsługi pętli aktualizacji i użyć go do zaktualizowania obszaru tekstowego. Problem polega jednak na tym, że tylko główny wątek może tak naprawdę modyfikować interfejs użytkownika, więc wątek pętli aktualizacji musi zasygnalizować, że wątek główny wykona aktualizację. Odbywa się to za pomocą modułu obsługi.
Sprawdź ten link: http://developer.android.com/guide/topics/ui/dialogs.html#
Kliknij sekcję „Przykład ProgressDialog z drugim wątkiem”. Jest to przykład tego, co musisz zrobić, z wyjątkiem okna dialogowego postępu zamiast pola tekstowego.
Nie rób tego Istnieje prosta klasa timerów, która robi to wszystko za Ciebie. To pytanie nie ma nic wspólnego z progresywnymi oknami dialogowymi lub dialogami.
Falmarri
Czy spojrzałeś na sekcję linku, który zamieściłem, czy widziałeś tylko okno dialogowe słowa i zakładasz? Kod jest w 100% odpowiedni. Również FYI, jeśli używasz timera, nadal tworzysz wątek do obsługi pętli aktualizacji. Nadal będziesz musiał korzystać z modułu obsługi opisanego w linku, który zamieściłem.
Nick
Niestety, linkowana strona nie zawiera już sekcji z wymienionym tytułem. Łącząc się z kodem, należy zawsze dołączać fragment klucza bezpośrednio w odpowiedzi.
Być może przydatne byłyby wcięcia i objaśnienia kodu.
Raul Rene,
0
Jeśli ktoś jest zainteresowany, zacząłem bawić się tworzeniem standardowego obiektu do uruchomienia w wątku interfejsu użytkownika działań. Wygląda na to, że działa dobrze. Komentarze mile widziane. Chciałbym, aby to było dostępne w projektancie układu jako komponent do przeciągnięcia na działanie. Nie mogę uwierzyć, że coś takiego już nie istnieje.
Widzę, że dodałeś to kilka lat po oryginalnym pytaniu i odpowiedziach. Dodaj wyjaśnienie, w jaki sposób ta odpowiedź różni się od innych odpowiedzi, które już tam były. Dlaczego dodałeś kolejną - jaką korzyść / kiedy jest użyteczna / jakie wady dostrzegłeś w innych odpowiedziach?
ToolmakerSteve
Po prostu udostępniam kod, który wykonuje tę samą pracę z innym podejściem. Ale ilekroć chcesz zaktualizować dane dowolnego widoku. Musisz użyć do tego programu obsługi. ponieważ wiele razy zauważyłem, że użycie timertask do aktualizacji widoku nie działa .. Metoda @ Dave.B jest bardziej poprawna według mojej wiedzy.
Odpowiedzi:
ok, ponieważ nie zostało to jeszcze wyjaśnione, istnieją 3 proste sposoby radzenia sobie z tym. Poniżej znajduje się przykład pokazujący wszystkie 3, a na dole przykład pokazujący tylko metodę, która moim zdaniem jest lepsza. Pamiętaj także o wyczyszczeniu zadań w trybie onPause, w razie potrzeby zapisując stan.
najważniejszą rzeczą do zapamiętania jest to, że interfejs użytkownika można modyfikować tylko z głównego wątku interfejsu użytkownika, więc użyj programu obsługi lub activity.runOnUIThread (Runnable r);
Oto, co uważam za preferowaną metodę.
źródło
To jest proste! Tworzysz nowy licznik czasu.
Następnie rozszerzasz zadanie timera
A następnie dodaj nowe zadanie do Timera z pewnym interwałem aktualizacji
Oświadczenie: To nie jest idealne rozwiązanie. Jest to rozwiązanie wykorzystujące klasę Timer (zgodnie z zapytaniem OP). W zestawie Android SDK zaleca się użycie klasy Handler (w zaakceptowanej odpowiedzi znajduje się przykład).
źródło
Jeśli musisz także uruchomić swój kod w wątku interfejsu użytkownika (a nie w wątku licznika czasu), zajrzyj na blog: http://steve.odyfamily.com/?p=12
źródło
Jeśli po prostu chcesz zaplanować odliczanie do czasu w przyszłości z regularnymi powiadomieniami o odstępach czasu po drodze, możesz użyć klasy CountDownTimer, która jest dostępna od poziomu API 1.
źródło
SS:MiMi
? DziękiOto prosty kod dla timera:
źródło
Myślę, że możesz to zrobić w Rx sposób:
I anuluj to tak:
Rx Timer http://reactivex.io/documentation/operators/timer.html
źródło
Ponieważ to pytanie wciąż przyciąga wielu użytkowników z wyszukiwarki Google (o zegarze Androida), chciałbym wstawić dwie monety.
Przede wszystkim klasa Timer będzie przestarzała w Javie 9 (przeczytaj zaakceptowaną odpowiedź) .
Urzędnik zaproponował sposobem jest użycie ScheduledThreadPoolExecutor który jest bardziej skuteczny i dysponuje bogatym w które można dodatkowo komendy rozkład jazdy po danym opóźnienie lub okresowo wykonywać. Ponadto zapewnia dodatkową elastyczność i możliwości ThreadPoolExecutor.
Oto przykład użycia prostych funkcji.
Utwórz usługę executora:
Po prostu zaplanuj uruchomienie:
Możesz teraz użyć,
future
aby anulować zadanie lub sprawdzić, czy zostało wykonane, na przykład:Mam nadzieję, że okaże się to przydatne do tworzenia zadań w Androidzie.
Kompletny przykład:
źródło
Dziwi mnie, że nie ma odpowiedzi, która wspomniałaby o rozwiązaniu z RxJava2 . To jest naprawdę proste i zapewnia łatwy sposób na ustawienie timera w Androidzie.
Najpierw musisz skonfigurować zależność Gradle, jeśli jeszcze tego nie zrobiłeś:
(wymienić
x
iy
z aktualnym numerem wersji )Ponieważ mamy tylko proste, BEZ POWTARZANIA ZADANIE , możemy użyć
Completable
obiektu:W przypadku POWTARZANIA ZADANIA można użyć
Observable
w podobny sposób:Schedulers.computation()
zapewnia, że nasz zegar działa na wątku w tle i.observeOn(AndroidSchedulers.mainThread())
oznacza, że kod uruchamiany po zakończeniu timera zostanie wykonany w głównym wątku.Aby uniknąć niechcianych wycieków pamięci, należy anulować subskrypcję, gdy Aktywność / Fragment zostanie zniszczona.
źródło
Subscription
zwrócony przez.subscribe()
metodę w zmiennej, a następnie wywołać,subscription.unsubscribe()
gdy chcesz zatrzymać stoper.Jest prostszym rozwiązaniem, działa dobrze w mojej aplikacji.
źródło
Chcesz, aby aktualizacje interfejsu użytkownika miały miejsce w już istniejącym wątku interfejsu użytkownika.
Najlepszym sposobem jest użycie modułu obsługi, który korzysta z funkcji postDelayed, aby uruchomić Runnable po opóźnieniu (każde uruchomienie planuje następne); wyczyść połączenie zwrotne za pomocą removeCallbacks.
Patrzysz już we właściwe miejsce, więc spójrz na to jeszcze raz, być może wyjaśnij, dlaczego ta próbka kodu nie jest tym, czego chcesz. (Zobacz także identyczny artykuł na temat aktualizacji interfejsu użytkownika z timera ).
źródło
Oto prosty, niezawodny sposób ...
Umieść następujący kod w swojej aktywności, a metoda tick () będzie wywoływana co sekundę w wątku interfejsu użytkownika, gdy twoja aktywność będzie w stanie „wznowionym”. Oczywiście możesz zmienić metodę tick (), aby robić to, co chcesz, lub być nazywanym mniej lub bardziej często.
Dla zainteresowanych kod „_h0 = _handler” jest niezbędny, aby uniknąć jednoczesnego działania dwóch timerów, jeśli aktywność zostanie wstrzymana i wznowiona w okresie tykania.
źródło
_h0
podejście, zamiastremoveCallbacks
wonPause
, jak wszyscy inni?Możesz także użyć do tego animatora:
źródło
Musisz utworzyć wątek do obsługi pętli aktualizacji i użyć go do zaktualizowania obszaru tekstowego. Problem polega jednak na tym, że tylko główny wątek może tak naprawdę modyfikować interfejs użytkownika, więc wątek pętli aktualizacji musi zasygnalizować, że wątek główny wykona aktualizację. Odbywa się to za pomocą modułu obsługi.
Sprawdź ten link: http://developer.android.com/guide/topics/ui/dialogs.html# Kliknij sekcję „Przykład ProgressDialog z drugim wątkiem”. Jest to przykład tego, co musisz zrobić, z wyjątkiem okna dialogowego postępu zamiast pola tekstowego.
źródło
źródło
Jeśli ktoś jest zainteresowany, zacząłem bawić się tworzeniem standardowego obiektu do uruchomienia w wątku interfejsu użytkownika działań. Wygląda na to, że działa dobrze. Komentarze mile widziane. Chciałbym, aby to było dostępne w projektancie układu jako komponent do przeciągnięcia na działanie. Nie mogę uwierzyć, że coś takiego już nie istnieje.
W tym działaniu mam onStart:
źródło
.xml
źródło
Jeśli masz już czas delta.
źródło
I abstrakcyjny Timer odszedł i uczynił z niego osobną klasę:
I wyodrębnij główną akcję z
Timer
klasy jakoI użyłem tego tak:
Mam nadzieję, że to pomaga 😊👌
źródło
Używam w ten sposób:
następnie poniżej onCreate
to prosty sposób na rozwiązanie tego problemu.
źródło
Dla tych, którzy nie mogą polegać na Chronometrze , stworzyłem klasę użyteczności na podstawie jednej z sugestii:
użyć… po prostu wykonaj:
.....
źródło