IllegalMonitorStateException

m0$tw4nt3d

Mitglied
Hi,
wenn ich versuche folgenden Code zu starten:
Java:
public class MainActivity extends AppCompatActivity {
    public EditText timer;
    public Button start;

    public boolean pressed;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        pressed = false;

        timer = findViewById(R.id.timer);
        start = findViewById(R.id.button);

        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                pressed = !pressed;
                start.setText(updatebuttonText(pressed));
                int secs = 0;
                while (pressed){
                    Timer time = new Timer();
                    
                    try {
                        time.wait(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    secs += 1;
                    if(secs%10 == 0 && !(secs%50 == 0)){
                        Toast.makeText(MainActivity.this, "10s", Toast.LENGTH_SHORT).show();
                    }else if(secs%50 == 0){
                        Toast.makeText(MainActivity.this, "50s", Toast.LENGTH_SHORT).show();
                    }
                    timer.setText(updateTimerText(secs));
                }
            }
        });
    }

    public String updateTimerText(int secs){
        int hours;
        int mins;
        int sec;

        if (secs < 60){
            return String.valueOf(secs);
        } else if (secs < 60 * 60) {
            mins = secs % 60;
            sec = secs - (mins*60);

            return "00:"+mins+":"+sec;
        } else {
            hours = secs % (60*60);
            mins = (secs - (60*hours)) % 60;
            sec = secs - (mins*60);

            return hours+":"+mins+":"+sec;
        }

    }
    public String updatebuttonText(boolean pressed){
        if(pressed){
            return "Stop";
        }else{
            return "Start";
        }
    }

Und dann den button drücke erhalte ich folgende Errormeldung:
Code:
Process: com.example.sportcounttimer, PID: 29640
java.lang.IllegalMonitorStateException: object not locked by thread before wait()
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:442)
at com.example.sportcounttimer.MainActivity$1.onClick(MainActivity.java:39)

Und ich wollte fragen, was ich im Code falsch gemacht habe, dass ich diese Errormeldung bekomme.

Danke für alle Ideen und Lösungen
 

thecain

Top Contributor
Du müsstest den Timer (welchen auch immer du verwendest) korrekt benutzen, statt die wait Methode von Object aufzurufen.
 

m0$tw4nt3d

Mitglied
Du müsstest den Timer (welchen auch immer du verwendest) korrekt benutzen, statt die wait Methode von Object aufzurufen.
Ich habe das jetzt mal so versucht:
Java:
public class MainActivity extends AppCompatActivity {
    public EditText timer;
    public Button start;

    public boolean pressed;
    Thread time = new Thread(){
        int secs = 0;
        public void run(){
            Looper.prepare();
            while(pressed){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Log.i("Exception", "Interrupted");
                }
                if(secs%10 == 0 && !(secs%50 == 0)){
                    Toast.makeText(MainActivity.this, "10s", Toast.LENGTH_SHORT).show();
                }else if(secs%50== 0){
                    Toast.makeText(MainActivity.this, "50s", Toast.LENGTH_SHORT).show();
                }
                timer.setText(updateTimerText(secs));
                ++secs;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        pressed = false;

        timer = findViewById(R.id.timer);
        start = findViewById(R.id.button);

        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                pressed = !pressed;
                start.setText(updatebuttonText(pressed));
                time.start();

            }
        });
    }

    public String updateTimerText(int secs){
        int hours;
        int mins;
        int sec;

        if (secs < 60){
            return "00:00:"+secs;
        } else if (secs < 60 * 60) {
            mins = secs % 60;
            sec = secs - (mins*60);

            return "00:"+mins+":"+sec;
        } else {
            hours = secs % (60*60);
            mins = (secs - (60*hours)) % 60;
            sec = secs - (mins*60);

            return hours+":"+mins+":"+sec;
        }

    }
    public String updatebuttonText(boolean pressed){
        if(pressed){
            return "Stop";
        }else{
            return "Start";
        }
    }

mit einem Thread allerdings bekomme ich nach ca. 50 sekunden jedes mal folgenden Error, den ich nicht zu ordnen kann, da ich keinen Array habe:
Code:
Process: com.example.sportcounttimer, PID: 32419
java.lang.IndexOutOfBoundsException: 1, 0
at android.text.PackedIntVector.getValue(PackedIntVector.java:75)
at android.text.DynamicLayout.getLineStart(DynamicLayout.java:1028)
at android.text.Layout.getLineEnd(Layout.java:1676)
 

KonradN

Super-Moderator
Mitarbeiter
Eigentliche alle UI Frameworks mögen es gar nicht, wenn UI Elemente aus einem anderen Thread verändert werden.

Activity hat dazu eine Methode runOnUiThread, die Du statt dessen nutzen müsstest um auf timer setText aufzurufen.
 

m0$tw4nt3d

Mitglied
Eigentliche alle UI Frameworks mögen es gar nicht, wenn UI Elemente aus einem anderen Thread verändert werden.

Activity hat dazu eine Methode runOnUiThread, die Du statt dessen nutzen müsstest um auf timer setText aufzurufen.
Danke. Wenn ich das mache, dann funktioniert es bis ich den button drücke. Wenn ich dann versuche mit erneutem drücken den Timer wieder zu starten erhalte ich wieder eine Error Meldung:
Java:
java.lang.IllegalThreadStateException

Code:
Java:
    Thread time = new Thread(){
        int secs = 0;
        public void run(){

            Looper.prepare();
            while(pressed){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Log.i("Exception", "Interrupted");
                }
                if(secs%10 == 0 && !(secs%50 == 0)){
                    Toast.makeText(MainActivity.this, "10s", Toast.LENGTH_SHORT).show();
                }else if(secs%50== 0){
                    Toast.makeText(MainActivity.this, "50s", Toast.LENGTH_SHORT).show();
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        timer.setText(updateTimerText(secs));
                    }
                });

                ++secs;
            }
        }
    };


//Button OnClickListener
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                pressed = !pressed;
                start.setText(updatebuttonText(pressed));
                if(pressed){
                    time.start();
                }

            }
        });
 

KonradN

Super-Moderator
Mitarbeiter
Hier würde ich Dir empfehlen, Dich einmal mit den entsprechenden Grundlagen der Klassen vertraut zu machen, die Du verwendest.

- Am Anfang wolltest Du einen Timer nutzen - aber hast Dir nicht einmal die Klasse Timer angesehen. Es gab dann Probleme mit der Methode wait und Du hast dir die Dokumentation zu der Methode nicht angesehen (Das ist eine Methode in Object, da findet sich schon einiges an Informationen zu wait / notify und wenn das nicht reichen würde, dann hat man auf jeden Fall eine Grundlage für weitere Rcherchen)

- Jetzt nutzt Du die Klasse Thread aber machst Dich damit nicht vertraut.

Du solltest Dir angewöhnen, immer in die Dokumentation zu schauen um Dir dann ein fundiertes Wissen zuzulegen. Software Entwicklung ohne ein Verständnis, was die verwendeten Klassen überhaupt machen, funktioniert nicht.

Das Problem ist einfach: Ein Thread hat ein fixes Zustandsmodell. Nach der Erstellung der Instanz ist der Thread Startbar. Dann kann man ihn Starten. Und dann ist der Thread irgendwann beendet. Und das war es. Man kann Treads nicht mehrfach starten!

Was Du also vermutlich machen willst:
a) den bestehenden Thread stoppen / beeden
b) eine neue Instanz erzeugen und diese dann starten.

Zu a) könnte man dem Thread eine Variable verpassen, die signalisiert, ob der Thread sich beenden soll. Das kann dann gesetzt werden und damit stoppt der Thread dann.
zu b) das sollte ja problemlos gehen. Du hast ja bereits den Code, der das macht.
 

lonLat

Mitglied
@m0$tw4nt3d Vergiss das mit den Threads. Du brauchst so etwas wie new Timer().scheduleAtFixedRate(task, after, interval);. Das Intervall sollte alle 10 Sekunden sein. In etwa so:

Java:
package org.example;

import java.util.Timer;
import java.util.TimerTask;

public class MyInterval {
    public static void main(String[] args) {
        new Timer().scheduleAtFixedRate(new TimerTask() {
            int seconds = 1;
            @Override
            public void run() {
                if (seconds % 50 == 0) {
                    // Möglicherweise auf dem UIT ausführen:
                    Toast.makeText(MainActivity.this, "50s um", Toast.LENGTH_SHORT).show();
                } else if (seconds % 10 == 0) {
                    // Möglicherweise auf dem UIT ausführen:
                    Toast.makeText(MainActivity.this, "10s um", Toast.LENGTH_SHORT).show();
                }
                seconds++;
            }
        }, 1_000, 10_000);
    }
}

(hier einmal normales SE als PoC, da ich gerade kein Android Studio installiert habe)

Und dann musst du dich noch darum kümmern, was geschehen soll, wenn die Activity das zeitliche segnet ... Stichwort: Lifecycles.

Zum Weiterlesen: https://stackoverflow.com/a/1884407
 

Jw456

Top Contributor
Ich finde es aber trotzdem falsch diesen Bezieichener zubenutzen.

Den Context der Activity hat er auch nicht in der Klasse. Uberveben hat er ihn auch nicht.
Und Ausgaben auf den Bildschirm nur im ui Thread. Er ist arber im timer Task was nicht der ui ist.
 

KonradN

Super-Moderator
Mitarbeiter
Es geht schlicht darum, das Konzept des Timers zu zeigen. Und das hat er mit Java SE gemacht. Der Hinweis hat früher einfach gefehlt und hätte deutlich früher kommen müssen. Nur auf das eigentliche Problem einzugehen ist einfach nicht ausreichend, wenn der TE da Dinge nachbaut, die es bereits fertig gibt.

Und ganz nebenbei: Statt einfach nur so über die Antwort eines Anderen zu meckern könntest Du eine bessere Antwort schreiben. Zeige einfach, wie es funktionieren kann. Wenn das Beispiel Dir nicht ausreicht, dann bring einfach ein besseres .... Wäre das nicht sinnvoller?

Aber ich denke, dieses Thema muss man nicht weiter vertiefen.
 

Neue Themen


Oben