Ich habe einen einfachen Musik-Player in Android gebaut. Die Ansicht für jedes Lied enthält eine SeekBar, die wie folgt implementiert ist:
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);
}
}
Das funktioniert gut. Jetzt möchte ich einen Timer, der die Sekunden / Minuten des Fortschritts des Songs zählt. Also habe ich ein TextView
in das Layout eingefügt, es mit findViewById()
in aufgenommen onCreate()
und dieses nach run()
eingefügt 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);
Aber diese letzte Zeile gibt mir die Ausnahme:
android.view.ViewRoot $ CalledFromWrongThreadException: Nur der ursprüngliche Thread, der eine Ansichtshierarchie erstellt hat, kann seine Ansichten berühren.
Trotzdem mache ich hier im Grunde das Gleiche wie beim SeekBar
- Erstellen der Ansicht onCreate
, dann Berühren run()
- und es gibt mir keine Beschwerde.
quelle
error.setText(res.toString());
Inside-the-Run () -Methode machen wollte, aber ich konnte die Res nicht verwenden, weil sie nicht endgültig war.myActivityObject.runOnUiThread(etc)
runOnUiThread()
eine Methode der Aktivität ist. Ich habe meinen Code in einem Fragment ausgeführt. Am Ende habe ich es getangetActivity().runOnUiThread(etc)
und es hat funktioniert. Fantastisch!;Ich habe das gelöst, indem ich
runOnUiThread( new Runnable(){ ..
hineingesteckt haberun()
:quelle
wait(5000);
sich nicht in Runnable befindet, da sonst Ihre Benutzeroberfläche während der Wartezeit einfriert. Sie sollten in Betracht ziehen,AsyncTask
für solche Vorgänge anstelle von Thread zu verwenden.Meine Lösung dafür:
Rufen Sie diese Methode in einem Hintergrundthread auf.
quelle
runOnUiThread
durchrunTestOnUiThread
. DankeNormalerweise muss jede Aktion, die die Benutzeroberfläche betrifft, im Haupt- oder UI-Thread ausgeführt werden, dh in dem, in dem die
onCreate()
Ereignisbehandlung ausgeführt wird. Eine Möglichkeit, dies sicherzustellen, ist die Verwendung von runOnUiThread () , eine andere die Verwendung von Handlern.ProgressBar.setProgress()
hat einen Mechanismus, für den es immer auf dem Hauptthread ausgeführt wird, deshalb hat es funktioniert.Siehe Schmerzloses Einfädeln .
quelle
Ich war in dieser Situation, habe aber mit dem Handler-Objekt eine Lösung gefunden.
In meinem Fall möchte ich einen ProgressDialog mit dem Beobachtermuster aktualisieren . Meine Ansicht implementiert Beobachter und überschreibt die Aktualisierungsmethode.
Also, mein Haupt-Thread erstellt die Ansicht und ein anderer Thread ruft die Update-Methode auf, die ProgressDialop aktualisiert und ....:
Es ist möglich, das Problem mit dem Handler-Objekt zu lösen.
Unten verschiedene Teile meines Codes:
Diese Erklärung finden Sie auf dieser Seite , und Sie müssen den "Beispiel-Fortschrittsdialog mit einem zweiten Thread" lesen.
quelle
Sie können den Handler verwenden, um die Ansicht zu löschen, ohne den Haupt-UI-Thread zu stören. Hier ist ein Beispielcode
quelle
Ich sehe, dass Sie die Antwort von @ providence akzeptiert haben. Für alle Fälle können Sie auch den Handler verwenden! Führen Sie zuerst die int-Felder aus.
Erstellen Sie als Nächstes eine Handlerinstanz als Feld.
Mach eine Methode.
Zum Schluss setzen Sie dies auf
onCreate()
Methode.quelle
Ich hatte ein ähnliches Problem und meine Lösung ist hässlich, aber es funktioniert:
quelle
Ich benutze
Handler
mitLooper.getMainLooper()
. Es hat gut für mich funktioniert.quelle
Verwenden Sie diesen Code und Sie müssen nicht
runOnUiThread
funktionieren:quelle
Dies löst explizit einen Fehler aus. Es heißt, welcher Thread eine Ansicht erstellt hat, nur dieser kann seine Ansichten berühren. Dies liegt daran, dass sich die erstellte Ansicht im Bereich des Threads befindet. Die Ansichtserstellung (GUI) erfolgt im UI-Thread (Hauptthread). Sie verwenden also immer den UI-Thread, um auf diese Methoden zuzugreifen.
Im obigen Bild befindet sich die Fortschrittsvariable im Bereich des UI-Threads. Daher kann nur der UI-Thread auf diese Variable zugreifen. Hier greifen Sie über den neuen Thread () auf den Fortschritt zu, und deshalb haben Sie eine Fehlermeldung erhalten.
quelle
Dies geschah mit meinem, als ich eine Änderung der Benutzeroberfläche von einem
doInBackground
vonAsynctask
anstelle von forderteonPostExecute
.Der Umgang mit der Benutzeroberfläche hat
onPostExecute
mein Problem gelöst.quelle
onPostExecute
ist auch eine Methode,AsyncTask
aber es läuft auf dem UI-Thread. Siehe hier: blog.teamtreehouse.com/all-about-android-asynctasksKotlin-Coroutinen können Ihren Code so übersichtlicher und lesbarer machen:
Oder umgekehrt:
quelle
Ich habe mit einer Klasse gearbeitet, die keinen Verweis auf den Kontext enthielt. So war es mir nicht möglich,
runOnUIThread();
ich benutzteview.post();
und es wurde gelöst.quelle
audioMessage
undtvPlayDuration
zum Fragencode?audioMessage
ist ein Halterobjekt der Textansicht.tvPlayDuration
ist die Textansicht, die wir von einem Nicht-UI-Thread aktualisieren möchten. In der obigen FragecurrentTime
handelt es sich um die Textansicht, die jedoch kein Inhaberobjekt enthält.Bei Verwendung von AsyncTask Aktualisieren Sie die Benutzeroberfläche in der onPostExecute- Methode
quelle
Ich hatte ein ähnliches Problem und keine der oben genannten Methoden funktionierte für mich. Am Ende hat dies den Trick für mich getan:
Ich habe dieses Juwel hier gefunden .
quelle
Dies ist die Stapelverfolgung der genannten Ausnahme
Wenn Sie also graben, lernen Sie es kennen
Wobei mThread im Konstruktor wie unten initialisiert wird
Ich möchte nur sagen, dass wir beim Erstellen einer bestimmten Ansicht diese im UI-Thread erstellt haben und später versuchen, sie in einem Worker-Thread zu ändern.
Wir können es über das folgende Code-Snippet überprüfen
wenn wir das Layout aufblasen und später, wo Sie eine Ausnahme bekommen.
quelle
Wenn Sie die
runOnUiThread
API nicht verwenden möchten , können Sie sie tatsächlichAsynTask
für die Vorgänge implementieren , deren Abschluss einige Sekunden dauert. In diesem Fall müssen Sie jedoch auch nach der Bearbeitung Ihrer Arbeit indoinBackground()
die fertige Ansicht zurückgebenonPostExecute()
. Die Android-Implementierung ermöglicht es nur dem Haupt-UI-Thread, mit Ansichten zu interagieren.quelle
Wenn Sie einfach ungültig machen (Repaint / Redraw-Funktion aufrufen) möchten, verwenden Sie postInvalidate ()
Dies wird eine ungültige Anfrage auf dem UI-Thread posten.
Für weitere Informationen: Was-macht-Postinvalidieren-tun
quelle
Für mich war das Problem, dass ich
onProgressUpdate()
explizit von meinem Code aus anrief . Dies sollte nicht getan werden. Ich habepublishProgress()
stattdessen angerufen und das hat den Fehler behoben.quelle
In meinem Fall habe ich
EditText
in Adapter, und es ist bereits im UI-Thread. Wenn diese Aktivität geladen wird, stürzt sie jedoch mit diesem Fehler ab.Meine Lösung ist, dass ich
<requestFocus />
EditText in XML entfernen muss.quelle
Für die Menschen in Kotlin funktioniert das folgendermaßen:
quelle
Gelöst: Fügen Sie diese Methode einfach in die doInBackround-Klasse ein ... und übergeben Sie die Nachricht
quelle
In meinem Fall wird der Fehler angezeigt, wenn der Anrufer in kurzer Zeit zu oft anruft. Ich habe einfach die verstrichene Zeit überprüft, um nichts zu tun, wenn es zu kurz ist, z. B. zu ignorieren, wenn die Funktion weniger als 0,5 Sekunden aufgerufen wird:
quelle
Wenn Sie keinen UIThread gefunden haben, können Sie diesen Weg verwenden.
Ihr aktueller Kontext bedeutet, dass Sie den aktuellen Kontext analysieren müssen
quelle
Wir müssen UI-Thread für den Job auf echte Weise verwenden. Wir können UI-Thread in Kotlin verwenden:
@canerkaseler
quelle
In Kotlin geben Sie einfach Ihren Code in die Aktivitätsmethode runOnUiThread ein
quelle