1208 Stimmen

Android "Nur der ursprüngliche Thread, der eine Ansichtshierarchie erstellt hat, kann seine Ansichten berühren."

Ich habe einen einfachen Musik-Player in Android gebaut. Die Ansicht für jeden Song enthält eine SeekBar, wie folgt implementiert:

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 Songfortschritts zählt. Also habe ich eine TextView im Layout, erhalten Sie es mit findViewById() en onCreate() und setzen Sie dies ein run() nach 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 ist die Ausnahme:

Android.view.ViewRoot$CalledFromWrongThreadException: Nur der ursprüngliche Thread, der eine View-Hierarchie erstellt hat, kann seine Views berühren.

Dennoch tue ich hier im Grunde das Gleiche wie bei der SeekBar - die Erstellung der Ansicht in onCreate und berührt sie dann in run() - und ich bekomme diese Beschwerde nicht.

7voto

Uddhav P. Gautam Punkte 6634

Dies führt explizit zu einem Fehler. Er besagt, dass nur derjenige Thread, der eine Ansicht erstellt hat, seine Ansichten berühren kann. Das liegt daran, dass die erstellte Ansicht innerhalb des Bereichs dieses Threads liegt. Die Erstellung der Ansicht (GUI) erfolgt im UI-Thread (Hauptthread). Sie verwenden also immer den UI-Thread, um auf diese Methoden zuzugreifen.

Enter image description here

In der obigen Abbildung befindet sich die Fortschrittsvariable innerhalb des Bereichs des UI-Threads. Daher kann nur der UI-Thread auf diese Variable zugreifen. Hier greifen Sie über new Thread() auf die Variable progress zu, weshalb Sie einen Fehler erhalten.

5voto

Jonathan DS Punkte 1934

Das ist mir passiert, als ich eine UI-Änderung bei einer doInBackground de Asynctask statt mit onPostExecute .

Der Umgang mit der UI in onPostExecute mein Problem gelöst.

5voto

Ifta Punkte 1426

Ich habe mit einer Klasse gearbeitet, die keinen Verweis auf den Kontext enthielt. Daher war es mir nicht möglich, die runOnUIThread(); Ich habe view.post(); und es wurde gelöst.

timer.scheduleAtFixedRate(new TimerTask() {

    @Override
    public void run() {
        final int currentPosition = mediaPlayer.getCurrentPosition();
        audioMessage.seekBar.setProgress(currentPosition / 1000);
        audioMessage.tvPlayDuration.post(new Runnable() {
            @Override
            public void run() {
                audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition));
            }
        });
    }
}, 0, 1000);

4voto

Deepak Kataria Punkte 524

Bei der Verwendung von AsyncTask Aktualisieren Sie die UI in onPostExecute Methode

    @Override
    protected void onPostExecute(String s) {
   // Update UI here

     }

3voto

Sam Punkte 452

Wenn Sie nicht möchten, dass runOnUiThread API, können Sie in der Tat implementieren AsynTask für die Vorgänge, die einige Sekunden in Anspruch nehmen. Aber in diesem Fall, auch nach der Verarbeitung Ihrer Arbeit in doinBackground() müssen Sie die fertige Ansicht in onPostExecute() . Die Android-Implementierung erlaubt nur dem Haupt-UI-Thread die Interaktion mit Ansichten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X