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.

3voto

Amit Yadav Punkte 28640

Dies ist die Stapelverfolgung der genannten Ausnahme

        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
        at android.view.View.requestLayout(View.java:16474)
        at android.view.View.requestLayout(View.java:16474)
        at android.view.View.requestLayout(View.java:16474)
        at android.view.View.requestLayout(View.java:16474)
        at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
        at android.view.View.requestLayout(View.java:16474)
        at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
        at android.view.View.setFlags(View.java:8938)
        at android.view.View.setVisibility(View.java:6066)

Wenn du also gehst und gräbst, erfährst du

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

Wo mThread wird im Konstruktor wie folgt initialisiert

mThread = Thread.currentThread();

Alles, was ich meine zu sagen, dass, wenn wir bestimmte Ansicht erstellt wir es auf UI-Thread erstellt und später versuchen, in einem Worker-Thread zu ändern.

Wir können dies mit dem folgenden Codeschnipsel nachprüfen

Thread.currentThread().getName()

wenn wir das Layout aufblasen und später, wenn Sie eine Ausnahme erhalten.

2voto

Nalin Punkte 105

Wenn Sie einfach ungültig machen wollen (repaint/redraw Funktion aufrufen) von Ihrem nicht UI Thread, verwenden Sie postInvalidate()

myView.postInvalidate();

Dadurch wird eine Ungültigmachungsanforderung an den UI-Thread gesendet.

Für weitere Informationen: was-macht-postinvalidate-do

2voto

rahat Punkte 1691

Nun, Sie können es so machen.

https://developer.Android.com/reference/Android/view/View#post(java.lang.Runnable)

Ein einfacher Ansatz

currentTime.post(new Runnable(){
            @Override
            public void run() {
                 currentTime.setText(time);     
            }
        }

sie bietet auch Verzögerung

https://developer.Android.com/reference/Android/view/View#postDelayed(java.lang.Runnable,%20long)

1voto

Tarun Kumar Punkte 498

Für die Leute, die sich in Kotlin abmühen, funktioniert es folgendermaßen:

lateinit var runnable: Runnable //global variable

 runOnUiThread { //Lambda
            runnable = Runnable {

                //do something here

                runDelayedHandler(5000)
            }
        }

        runnable.run()

 //you need to keep the handler outside the runnable body to work in kotlin
 fun runDelayedHandler(timeToWait: Long) {

        //Keep it running
        val handler = Handler()
        handler.postDelayed(runnable, timeToWait)
    }

1voto

Udara Kasun Punkte 2073

Wenn Sie keinen UIThread finden konnten, können Sie diesen Weg benutzen.

yourcurrentcontext bedeuten, müssen Sie Current Context analysieren

 new Thread(new Runnable() {
        public void run() {
            while (true) {
                (Activity) yourcurrentcontext).runOnUiThread(new Runnable() {
                    public void run() { 
                        Log.d("Thread Log","I am from UI Thread");
                    }
                });
                try {
                    Thread.sleep(1000);
                } catch (Exception ex) {

                }
            }
        }
    }).start();

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