Zbudowałem prosty odtwarzacz muzyki na Androida. Widok każdej piosenki zawiera SeekBar, zaimplementowany w następujący sposób:
public class Song extends Activity implements OnClickListener,Runnable {
private SeekBar progress;
private MediaPlayer mp;
// ...
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer
progress.setVisibility(SeekBar.VISIBLE);
progress.setProgress(0);
mp = appService.getMP();
appService.playSong(title);
progress.setMax(mp.getDuration());
new Thread(Song.this).start();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
// ...
progress = (SeekBar) findViewById(R.id.progress);
// ...
}
public void run() {
int pos = 0;
int total = mp.getDuration();
while (mp != null && pos<total) {
try {
Thread.sleep(1000);
pos = appService.getSongPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(pos);
}
}
To działa dobrze. Teraz chcę licznik sekund / minut postępu utworu. Więc umieścić TextView
w układzie, dostać go findViewById()
w onCreate()
, i umieścić to w run()
po progress.setProgress(pos)
:
String time = String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(pos),
TimeUnit.MILLISECONDS.toSeconds(pos),
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(
pos))
);
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
Ale ten ostatni wiersz daje mi wyjątek:
android.view.ViewRoot $ CalledFromWrongThreadException: Tylko oryginalny wątek, który utworzył hierarchię widoków, może dotykać jego widoków.
Ale robię tu zasadniczo to samo, co robię SeekBar
- tworząc widok onCreate
, a następnie dotykając go run()
- i to nie daje mi tej skargi.
źródło
error.setText(res.toString());
run (), ale nie mogłem użyć res, ponieważ nie była to ostateczna .. szkodamyActivityObject.runOnUiThread(etc)
runOnUiThread()
jest to metoda działania. Uruchomiłem mój kod we fragmencie. Skończyło się na tymgetActivity().runOnUiThread(etc)
i zadziałało. Fantastyczny!;Rozwiązałem to, umieszczając w
runOnUiThread( new Runnable(){ ..
środkurun()
:źródło
wait(5000);
nie ma go w Runnable, w przeciwnym razie interfejs użytkownika zawiesi się podczas okresu oczekiwania. Powinieneś rozważyć użycieAsyncTask
zamiast Wątku do takich operacji.Moje rozwiązanie tego:
Wywołaj tę metodę w wątku w tle.
źródło
runOnUiThread
gorunTestOnUiThread
. DziękiZwykle wszelkie działania związane z interfejsem użytkownika muszą być wykonywane w wątku głównym lub interfejsie użytkownika, czyli w tym
onCreate()
wykonywana jest obsługa zdarzeń. Jednym ze sposobów, aby być tego pewnym, jest użycie runOnUiThread () , innym sposobem jest użycie handlerów .ProgressBar.setProgress()
ma mechanizm, dla którego zawsze będzie wykonywany w głównym wątku, dlatego działał.Zobacz Bezbolesne nawlekanie .
źródło
Byłem w tej sytuacji, ale znalazłem rozwiązanie z Handler Object.
W moim przypadku chcę zaktualizować ProgressDialog o wzorzec obserwatora . Mój widok implementuje obserwatora i przesłania metodę aktualizacji.
Tak więc mój główny wątek tworzy widok, a inny wątek wywołuje metodę aktualizacji, która aktualizuje ProgressDialop i ....:
Możliwe jest rozwiązanie problemu z Obiektem Handler.
Poniżej różne części mojego kodu:
To wyjaśnienie można znaleźć na tej stronie i należy przeczytać „Przykładowy program ProgressDialog z drugim wątkiem”.
źródło
Za pomocą modułu obsługi można usunąć widok bez zakłócania głównego wątku interfejsu użytkownika. Oto przykładowy kod
źródło
Widzę, że zaakceptowałeś odpowiedź @ opatrzności. Na wszelki wypadek możesz również użyć obsługi! Najpierw wykonaj pola int.
Następnie utwórz instancję modułu obsługi jako pole.
Zrób metodę.
Na koniec umieść to w
onCreate()
metodzie.źródło
Miałem podobny problem i moje rozwiązanie jest brzydkie, ale działa:
źródło
Używam
Handler
zLooper.getMainLooper()
. Dla mnie działało dobrze.źródło
Użyj tego kodu i nie musisz
runOnUiThread
działać:źródło
To jawnie generuje błąd. Mówi, który wątek utworzył widok, ale tylko ten może dotknąć jego widoków. Jest tak, ponieważ utworzony widok znajduje się w przestrzeni tego wątku. Tworzenie widoku (GUI) odbywa się w wątku interfejsu użytkownika (głównym). Tak więc zawsze używasz wątku interfejsu użytkownika, aby uzyskać dostęp do tych metod.
Na powyższym zdjęciu zmienna progress znajduje się w przestrzeni wątku interfejsu użytkownika. Tak więc tylko wątek interfejsu użytkownika może uzyskać dostęp do tej zmiennej. Tutaj uzyskujesz dostęp do postępu za pomocą nowego Thread () i dlatego wystąpił błąd.
źródło
Stało się tak, gdy wezwałem do zmiany interfejsu użytkownika
doInBackground
zAsynctask
zamiast zamiast używaniaonPostExecute
.Radzenie sobie z interfejsem użytkownika
onPostExecute
rozwiązało mój problem.źródło
onPostExecute
jest to metoda,AsyncTask
ale działa w wątku interfejsu użytkownika. Zobacz tutaj: blog.teamtreehouse.com/all-about-android-asynctasksKortyny Kotlina mogą uczynić twój kod bardziej zwięzłym i czytelnym w następujący sposób:
Lub odwrotnie:
źródło
Pracowałem z klasą, która nie zawierała odniesienia do kontekstu. Więc nie mogłem użyć,
runOnUIThread();
którego użyłemview.post();
i został rozwiązany.źródło
audioMessage
itvPlayDuration
do kodu pytania?audioMessage
jest obiektem posiadacza widoku tekstowego.tvPlayDuration
to widok tekstu, który chcemy zaktualizować z wątku innego niż interfejs użytkownika. W powyższym pytaniucurrentTime
jest widok tekstu, ale nie ma on obiektu uchwytu.Podczas korzystania z AsyncTask zaktualizuj interfejs użytkownika w metodzie onPostExecute
źródło
Miałem podobny problem i żadna z wyżej wymienionych metod nie działała dla mnie. W końcu załatwiłem sprawę:
Znalazłem ten klejnot tutaj .
źródło
Jest to ślad stosu wspomnianego wyjątku
Więc jeśli idziesz i kopiesz, to wiesz
Gdzie mThread jest inicjowany w konstruktorze jak poniżej
Chciałbym tylko powiedzieć, że kiedy stworzyliśmy konkretny widok, utworzyliśmy go w wątku interfejsu użytkownika, a później próbowaliśmy zmodyfikować w wątku roboczym.
Możemy to zweryfikować za pomocą fragmentu kodu poniżej
kiedy nadmuchujemy układ i później, kiedy dostajesz wyjątek.
źródło
Jeśli nie chcesz używać
runOnUiThread
interfejsu API, możesz w rzeczywistości zaimplementowaćAsynTask
operacje, których wykonanie zajmuje kilka sekund. Ale w takim przypadku, również po przetworzeniu pracydoinBackground()
, musisz zwrócić gotowy widokonPostExecute()
. Implementacja Androida pozwala na interakcję tylko z głównym wątkiem interfejsu użytkownika z widokami.źródło
Jeśli chcesz po prostu unieważnić (funkcja odmalowania / przerysowania) w swoim wątku innym niż interfejs użytkownika, użyj postInvalidate ()
Spowoduje to opublikowanie nieprawidłowego żądania w wątku interfejsu użytkownika.
Aby uzyskać więcej informacji: what-does-postinvalidate-do
źródło
Dla mnie problemem było to, że dzwoniłem
onProgressUpdate()
jawnie z mojego kodu. Nie należy tego robić. ZadzwoniłempublishProgress()
zamiast tego i to rozwiązało błąd.źródło
W moim przypadku mam
EditText
w adapterze i jest już w wątku interfejsu użytkownika. Jednak po załadowaniu tego działania następuje awaria z tym błędem.Moje rozwiązanie polega na tym, że muszę usunąć
<requestFocus />
z EditText w XML.źródło
Dla osób walczących w Kotlinie działa to w następujący sposób:
źródło
Rozwiązane: wystarczy umieścić tę metodę w klasie doInBackround ... i przekazać wiadomość
źródło
W moim przypadku dzwoniący zbyt wiele razy w krótkim czasie otrzyma ten błąd, po prostu odkładam sprawdzanie czasu, aby nic nie robić, jeśli jest zbyt krótki, np. Zignoruj, jeśli funkcja zostanie wywołana krócej niż 0,5 sekundy:
źródło
Jeśli nie możesz znaleźć UIThread, możesz użyć tego sposobu.
Twój aktualny kontekst oznacza, że musisz przeanalizować bieżący kontekst
źródło
Musimy używać Wątku interfejsu użytkownika do zadania w prawdziwy sposób. Możemy użyć wątku interfejsu użytkownika w Kotlin:
@canerkaseler
źródło
W Kotlin po prostu umieść kod w metodzie działania runOnUiThread
źródło