Timer und setText funktionieren nicht in Kombination

znieh99

znieh99

Fortgeschrittenes Mitglied
12
Hallo Forum,
ich mache meine ersten Schritte mit Android und versuche eine Timer-Funktion zu programmieren, welche nach einem Button-Klick mit 3 Sekunden Verzögerung einen Wert in einem TextView ausgibt welcher immer um 1 erhöht wird.
Das Ergebnis nach dem ersten Klick ist, dass der Emulator "Unfortunately, TestTimer has stopped." ausgibt und die Konsole folgende Meldungen:
at android.widget.TextView.setText(TextView.java:4174)
at com.example.heinz.testtimer.MainActivity.showWert(MainActivity.java:28)
at com.example.heinz.testtimer.MainActivity$RunTimer.run(MainActivity.java:38)
at java.util.Timer$TimerImpl.run(Timer.java:284)
01-20 08:52:12.320 4437-4456/com.example.heinz.testtimer E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb4016b40

Ich habe im Code die Zeilen 28 und 38 gekennzeichnet.

Code:
package com.example.heinz.testtimer;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

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

public class MainActivity extends AppCompatActivity {
    Timer timer;
    int wert;
    public TextView tx;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("Test Position anzeigen");
        timer = new Timer();
    }

    public void showWert() {
        wert += 1;
        tx = (TextView) findViewById(R.id.text_feld_name);
        tx.setText(String.valueOf(wert));                   //Zeile 28
    }

    public void onClickStartTim(View view) {
        timer.schedule (new RunTimer(), 5000);
//        showWert();                    

    }
    class RunTimer extends  TimerTask {
        public void run() {
            showWert();                                     //Zeile 38
        }
    };
}
 
Zuletzt bearbeitet von einem Moderator:
Bearbeitet von: hagex - Grund: Edit, Gruß von hagex
Ich will mich jetzt nicht zu weit aus dem Fenster lehnen, da ich selbst noch Anfänger bin, aber fehlt bei dir nicht folgende Zeile am Anfang in OnCreate?
Code:
tx = (TextView) findViewById(R.id.textView)

EDIT: Hm okay, vielleicht sollte ich genauer hinschauen. Du hast diese Zeile drin. Aber erst in deiner Methode. Keine Ahnung, ob das so funktioniert.
EDIT2: Wo ist denn eigentlich der Button, auf den du klickst?
 
Zuletzt bearbeitet:
Zu 1: Ja das funktioniert
Zu 2: Der Button ist im layout.xml definiert.

EDIT: Hm okay, vielleicht sollte ich genauer hinschauen. Du hast diese Zeile drin. Aber erst in deiner Methode. Keine Ahnung, ob das so funktioniert.
EDIT2: Wo ist denn eigentlich der Button, auf den du klickst?[/QUOTE]
 
Bin ich jetzt nicht genug eingelesen oder weist du etwas, was ich nicht weiß? Um dem Button Funktionen zuzuweisen, musst du ihn in deiner Activity genauso deklarieren, wie dein TextView, oder bin ich da falsch gewickelt? :D

Deine XML ist ja in diesem Fall fürs Layout zuständig und nicht dafür, was passiert, wenn der Button geklickt wird.

Aber ich lasse mich gerne belehren. :)
 
In der XML kannst du auch einen onClick Namen angeben ( android:onClick="onClickStartTim" den du in Java als Methode definierst.
 
Zuletzt bearbeitet von einem Moderator:
  • Danke
Reaktionen: lordzwieback
znieh99 schrieb:
In der XML kannst du auch einen onClick Namen angeben ( android:onClick="onClickStartTim" den du in Java als Methode definierst.
Wieder was gelernt, bedankt. :)
 
onClickStartTim() so ändern:
Code:
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timer.timer.schedule (new RunTimer(), 5000);

und onPause() so ändern:
Code:
if (timer !=null) {
timer.cancel();
timer = null; //(Muss nicht unbedingt.)
}
 
Zuletzt bearbeitet:
@Markus,

ich überlege mir eben , ob wir ihn nicht direkt zum AsyncTask schicken ..........
 
swa00 schrieb:
@Markus,

ich überlege mir eben , ob wir ihn nicht direkt zum AsyncTask schicken ..........
Ich habe mir den Async Task bereits angesehen, aber da kann ich auch nur Werte zurückgeben und keine Methode aus dem UI-Task aufrufen.
 
ch habe mir den Async Task bereits angesehen, aber da kann ich auch nur Werte zurückgeben und keine Methode aus dem UI-Task aufrufen.

das ist nicht richtig Heinz,

Da kannst du ohne Weiters so etwas mit machen

Code:
 @Override
        protected Integer doInBackground(Void... params)
        {

            mActivity.runOnUiThread(new Runnable()
            {
              public void run()
              {
                //  do my UI stuff
              }   
            }
         }
                  }
 
  • Danke
Reaktionen: znieh99
Ich habe es nun mit dem AsyncTask probiert und bekomme vom Editor folgende Meldung: Die Class WarteClass oder die Methode doInBackground muss Abstract sein.
Code:
package com.example.heinz.testtimer2;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private Integer wert;
    private TextView wertTV;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        wertTV = (TextView) findViewById(R.id.text_feld_name);

    }

    public void onClickStartTimer(View v) {
        new WarteClass().execute(wert);
    }

    private class WarteClass extends AsyncTask<Integer, Integer, Integer> {   // muss Abstract sein?
        /** The system calls this to perform work in a worker thread and
        * delivers it the parameters given to AsyncTask.execute() */
        
        protected Integer doInBackground(Integer i) {      // oder hier Abstract ?
            return i;
        }

        /** The system calls this to perform work in the UI thread and delivers
         * the result from doInBackground() */
         protected void onPostExecute(Integer result) {
            wertTV.setText(result);
         }
    }
}
 
Probiers mal so:
Code:
@Override
protected Void doInBackground(String... strings) {
    return null;
}

Tipp von swa00 an mich vor ein paar Tagen: Versuch mal mit STRG + O deine "Untertasks" im AsyncTask einzubinden. Seit ich das so mache habe ich auch keine Probleme mehr mit meinen Tasks. Hatte (unter anderem) auch die Meldung von dir.
 
  • Danke
Reaktionen: znieh99
@lordzwieack

Schön machste das , ich lass dich mal machen - kommt gut:)
(Willkommen im Team - und Hut ab bzgl. deinen Fortschritten)
 
  • Danke
Reaktionen: lordzwieback
@lordzwieack
Danke: STRG + O hat das Abstract Problem gelöst!
 
  • Danke
Reaktionen: lordzwieback
@swa00
Es ist mir nicht klar, wie ich zu der Referenz "mActivity" in deinem Vorschlag (unten) komme!
Und verstehe ich das richtig, dass der Code in run der ja in einem eigenen Thread läuft, dann im UI-Thread läuft?
lg heinz

@override
protected Integer doInBackground(Void... params)
{

mActivity.runOnUiThread(new Runnable()
{
public void run()
{
// do my UI stuff
}
}
}
}
 
Moin Heinz,

Der UI THread ist ein anderer, als dein Timer Thread.
Deshalb musst du bei UI elementen auch diesen UI Thread ansprechen.

mActivity ist dein Zeiger auf die Activity

Deklaration
private Actvity mActivity = null

Zuweisung: (in onCreate)
mActivity = this;
 
  • Danke
Reaktionen: znieh99
Ich habe es nun geschafft, dass der AsyncTask in meinem Testbeispiel funktioniert und über
"mActivity.runOnUiThread(new Runnable()" einen Wert im Main Thread anzeigt. Wenn ich aber die Methode "onPostExecute()" dazu verwende, sieht es so aus als ob "onPostExecute()" vor "doInBackground()" läuft, da die Ausgabe immer um 1 Wert zu gering ist (also 0 statt 1). Wie ist das möglich?
LG Heinz
Code:
package com.example.heinz.testtimer2;

import android.app.Activity;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private Integer wert = 0;
    private TextView wertTV;
    private Activity mActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mActivity = this;                       //Referenz auf sich selbst (Main-Task)
        wertTV = (TextView) findViewById(R.id.text_feld_name);

    }

//    public void showWert() {
//        wert += 1;
//        wertTV.setText(String.valueOf(wert));
//    }

    /**
     * Der Start-Button wurde betätigt
     * Aufruf eines neuen Thread
     * @param v
     */
    public void onClickStartTimer(View v) {
        new WarteClass().execute(0);
    }

    /**
     * Diese class startet einen thread und gibt einen Wert im main-thread aus
     */
    private class WarteClass extends AsyncTask<Integer, Void, Integer> {
        /**
         * The system calls this to perform work in a worker thread and
         * delivers it the parameters given to AsyncTask.execute()
         */
        @Override
        protected Integer doInBackground(Integer... params) {
            mActivity.runOnUiThread(new Runnable() {
                public void run() {
                    wert += 1;
//                    wertTV.setText(String.valueOf(wert));
                }
            });
            return wert;
        }           //end M doInBackground

        /** The system calls this to perform work in the UI thread and delivers
         * the result from doInBackground() */
        @Override
        protected void onPostExecute(Integer i) {
            super.onPostExecute(i);
            wertTV.setText(String.valueOf(i));

        }           //end M onPostExecute
    }           //end class WarteClass
}           //end class MainActivity
 
Moin, Moin,

mWert ist nicht thread sicher. Sprich bei Threads kann eine Ecke schief laufen. Du muss dafür sorgen, dass jeweils nur ein Thread den Wert mWert setzen kann. Das geht z.B. mit AtomicInteger.
AtomicInteger | Android Developers

Und warum setzt du die Methode wertTV.setText() zwei mal auf?
 
  • Danke
Reaktionen: znieh99
markus.tullius schrieb:
Und warum setzt du die Methode wertTV.setText() zwei mal auf?
Hallo markus,
ich verändere "wert" nur an einer Position (runOnUiThread). wertTV.setText ist auch nur einmal "aktiv". Das Andere ist Inaktiv.
Ich habe einen Debuger-Stop in "onPostExecute()" gesetzt: Dort ist der Input-Parameter "0", Der "wert" Inhalt ist aber bereits "1" und der wurde ja übergeben.
LG heinz
 
Zuletzt bearbeitet von einem Moderator:
Erklärung für dein Bug. runOnUiThread() wir erst nach dem Aufruf von onPostExecute() ausgeführt. Der Grund ist die Nebenläufigkeit. Sobald Thread und Handler im Spiel sind entscheidet das Sytem, welchen Code es ausführen möchte. Es gibt bei Thread keine Gleichzeitigkeit. Am besten du liest Dich da mal rein.

Hast du schon mal versucht, den ganzen Thread foobar wegzulassen? Im Endeffekt macht der folgende Code das gleiche:
Code:
onClickStartTimer(View v) {
     wert++; // besser als wert += 1;
    wertTV.setText(String.valueOf(w));

}
 
  • Danke
Reaktionen: znieh99

Ähnliche Themen

C
  • CoderboyPB
Antworten
3
Aufrufe
915
swa00
swa00
SaniMatthias
Antworten
19
Aufrufe
871
swa00
swa00
D
Antworten
23
Aufrufe
2.383
Data2006
D
Zurück
Oben Unten