verwünschte App? Textviews zu langsam?

  • Antworten:8
  • Bentwortet
Bernd Schubert
  • Forum-Beiträge: 4

12.12.2017, 20:15:37 via Website

Hey, ich habe in einer von mir entwickelten App einen Fehler entdeckt, der mich nicht mehr schlafen läßt und an den Grundfesten verzweifeln.
Also die App liest aus einer SQLite-Datenbank Sprüche also Text und zeigt diese periodisch
an (Weiterschalten mittels Timer). Man kann komplizierte Einstellungen machen, aber das Grundproblem ist immer gleich: "Manchmal" werden aus einer Reihe von 10 eben nicht alle
angezeigt, nur 1-3-5-8-10 oder so, "manchmal" sind sie jedoch alle korrekt. Ich habe schon alles untersucht. Das Timer-Intervall, ist korrekt, die Methoden werden in der richtigen Reihenfolge durchlaufen, die Textviews regelmäßig gefüllt - nur anscheinend nicht immer angezeigt. So als wenn die Aktualisierung zu langsam ist. Ich habe auch schon die Datenbank abgeklemmt und lasse konstante Strings ausgeben. Habe einen Thread eingebaut, der sich schneller (jede Sekunde) als die Spruchaktualisierung (5 s) meldet und
noch mal die Infos in die Textviews schreibt. Ich hatte auch schon gedacht, mein Sprüche-Aktualisierungs-Threadworker existiert mehrfach oder ruft wenigstens mehrere Instanzen des Handlers auf, aber nein eigentlich nicht, oder ich kann ihn nicht sehen. Was mir auffiel, meine Anzeige-Aktivity geht im Debugger ziemlich willkürlich noch einmal durch onResume usw. und ist nach Finish noch ein paar Takte trotzdem "lebendig", aber da passiert alles nichts.
Hat jemand eine Idee, wie ich das Anzeigen der Textviews unmittelbar nach Befüllen erzwingen kann?
Gibt es eine eindeutigere Methode, periodisch meine Aktualisierung aufzurufen als den Threadworker (ich trau dem nicht, ist aber auf einem "Crashkurs Android" damals gewachsen, so 4 Wochen, wo ich das gelernt habe ). Es muß aber abbrechbar bzw. abklemmbar bleiben (ich entscheide bisher einfach im Handler, ob ausgewertet werden soll oder nicht und lass den Threadworker weiter laufen).
Wer meinen Code braucht? Leider sehe ich hier keine Möglichkeit das zu posten

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

12.12.2017, 21:07:43 via Website

Hallo Bernd,
herzlich wilkommen hier im Forum :)



Zuderstmal ist dein Code wichitg,den kannst du doch hier in Textform als "Code" (Editor) markiert posten.

Zudem sei gesagt, eine TextView aktualisiert sich sofort wenn du TextView#setText aufrufst.
Vorausgesetzt du bist im UI Thread.
Solltest du mit Threads arbeiten, musst du darauf achten, dass du immer zum Setzen des Textes in dem Main UI Thread zurückgehst z.b. mit runOnUiThread + Callbacks etc.

Hast du zum Nachforschen des Problems schonmal Logs in eine Datei geschrieben und immer dann wenn setText aufgerufen wird?
Ich vermute stark, dass da eher in deiner Logik ein fehler ist, als in den TextViews...

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Bernd Schubert
  • Forum-Beiträge: 4

12.12.2017, 22:06:19 via Website

Hallo Pascal, also hier meine beiden Klassen, die sich mit dem Thread beschäftigen.
Natürlich habe ich kein runOnUiThread drin, wie macht man das?
Ich glaube ich verstehe, was Du meinst. Der Thread ruft die Methode doBlaettern der Aktivity auf,
gibt der aber keine Chance zum Aktualisieren der Textview, richtig? Wie ändere ich das?

Und naja, die Logik - dafür bräuchtest du vielleicht mal die gesamte App. Aber eigentlich habe ich mit dem Debug im Android Studio nachvollzogen, daß er die Textviews immer regulär füllt. Nur zeigt das AVD und natürlich auch mein Handy das nicht immer an. "Manchmal" gehts ja. Da läuft er 1-2-3.... 10 durch. Nur "manchmal" zeigt er die 1, verhaspelt sich dann irgendwo und fängt unregelmäßig an zu zeigen, 3-5-6... oder so, als wenn er bei der 2 und 4 blind war oder schlafend. Ich kann die Folge anhalten (bAutomatik) und dann zur 2 zurückblättern.
Die Inhalte sind alle da. Ich hab sowas ähnliches wie Log gemacht - die Inhalte der Textviews (gekürzt) gekettet in eine String und die sogar in einer weiteren TV ausgegeben, damit das Handy sie zeigt.

package de.alfware.bernd.zitadell;

public class ThreadWorker extends Thread {

MActDisplay mActD;
public boolean bRun;
public int iDauer;

ThreadWorker(MActDisplay mActD) {
    this.mActD = mActD;
    this.bRun = true;
    this.iDauer = 1111;
}

public void run() {
    while (bRun) {
        try {
            Thread.sleep(iDauer);
            mActD.zitateHandler.sendEmptyMessage(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ;
    }
}

}

package de.alfware.bernd.zitadell;

import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Random;

public class MActDisplay extends ActionBarActivity implements GestureOverlayView.OnGesturePerformedListener, View.OnClickListener {

AppGlobal myApp;

ThreadWorker thWrk;
TextView tvCountDown, tvKategorie, tvSelected, tvID, tvAnzeige, tvNummer,
         tvAutomat, tvRichtung;
ScrollView scScroll;
ImageButton ibBack;
GestureLibrary gestureLib;

private Random r = new Random(System.currentTimeMillis());

Handler zitateHandler = new Handler() {
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 0:
                break;
            case 1:
                break;
        }

        //Der Thread soll weiterlaufen, es wird halt nur nicht ausgewertet/geblättert
        //Manuell: Thread komplett ignorieren
        //Nicht Manuell: nur Blättern, wenn die Automatik an ist
        if (!myApp.bManuell)
            if (myApp.bAutomatik)
                doBlaettern();
    }
};

private void doCountDown() {
    //Log.d("Zitat", "Hallo CountDown " + myApp.lAktListePos);
    if (myApp.lZeigePos > myApp.lZeigeMax) {
        pauseTimer();
        //das finish() scheint nur eine freundliche Empfehlung zu sein und verzögert zu wirken
        if (myApp.bFinish) {
            myApp.bFinish = false;
            this.finish();
        }
    } else {
        tvCountDown.setText("Zeige " + myApp.lZeigePos + " von " + myApp.lZeigeMax);
    }
    myApp.lZeigePos++;
}

private void pauseTimer() {
    thWrk.interrupt();
    thWrk.bRun = false;
    thWrk.iDauer = 999999;
    myApp.lZeigePos = myApp.lZeigeMax + 1;
}

private void recallTimer() {
    thWrk.bRun = true;
    thWrk.iDauer = myApp.iDauer;
    //myApp.lZeigePos = 1;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    myApp = (AppGlobal) getApplication();

    GestureOverlayView gestureOverlayView = new GestureOverlayView(this);
    View inflate = getLayoutInflater().inflate(R.layout.activity_mact_display, null);

    gestureOverlayView.addView(inflate);
    gestureOverlayView.addOnGesturePerformedListener(this);

    gestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures2);
    if (!gestureLib.load()) {
        myApp.bFinish = false;
        finish();
    }

    setContentView(gestureOverlayView);
    tvCountDown = (TextView) findViewById(R.id.tvStatus);
    tvKategorie = (TextView) findViewById(R.id.tvKategorie);
    tvNummer = (TextView) findViewById(R.id.tvNummer);
    tvSelected = (TextView) findViewById(R.id.tvSelected);
    tvID = (TextView) findViewById(R.id.tvID);
    tvAnzeige = (TextView) findViewById(R.id.tvAnzeige);
    tvAutomat = (TextView) findViewById(R.id.tvRichtungAuto);
    tvRichtung = (TextView) findViewById(R.id.tvRichtungVR);
    scScroll = (ScrollView) findViewById(R.id.scScrollView);

    myApp.lZeigePos = 1;
    myApp.lZeigeMax = 1;

    myApp.bAutomatik = true;
    myApp.bFinish = true;
    myApp.bVor = true;

    //es könnten sich falsche Wunsch-Parameter eingeschlichen haben z.B. über die Prefs
    if (myApp.lWahlKategorie < myApp.chDB.getMinKategorie(myApp.lMinQuelle, myApp.lMaxQuelle) ||
        myApp.lWahlKategorie > myApp.chDB.getMaxKategorie(myApp.lMinQuelle, myApp.lMaxQuelle))
        myApp.lWahlKategorie = -1;
    if (myApp.lWahlNummer < myApp.chDB.getMinNummer(myApp.lMinQuelle, myApp.lMaxQuelle) ||
        myApp.lWahlNummer > myApp.chDB.getMaxNummer(myApp.lMinQuelle, myApp.lMaxQuelle))
        myApp.lWahlNummer = -1;

    myApp.lAktMax = myApp.chDB.getAnzahl(myApp.lMinQuelle, myApp.lMaxQuelle,
            myApp.lWahlKategorie, myApp.lWahlNummer, myApp.bStichwort, myApp.sStichwort);

    if (myApp.lAktMax > 0) {
        if (myApp.lAktMax > myApp.lAnzahl){
            myApp.lZeigeMax = myApp.lAnzahl;
        } else {
            myApp.lZeigeMax = myApp.lAktMax;
        }
        anzeigeSpruch(true);

        ibBack = (ImageButton) findViewById(R.id.ibManuell);
        ibBack.setOnClickListener(this);
        ibBack.setVisibility(View.VISIBLE);

        thWrk = new ThreadWorker(this);
        thWrk.iDauer = myApp.iDauer;
        thWrk.start();

    } else {
        Toast.makeText(MActDisplay.this, "Es sind mit dem Stichwort " + myApp.sStichwort +
                        " nicht genügend Zitate vorhanden.", Toast.LENGTH_LONG).show();

        myApp.bFinish = false;
        this.finish();
    }
}

private void anzeigeSpruch(boolean neu) {
    Zitat aktZitat = null;

    if (neu) {
        aktZitat = myApp.chDB.getZitat(myApp.lMinQuelle, myApp.lMaxQuelle,
                myApp.lWahlKategorie, myApp.lWahlNummer,
                myApp.bStichwort, myApp.sStichwort,
                myApp.bVor, (!myApp.bLaufend), myApp.lAktPos);
        //Zurücklesen der neuen Pos
        myApp.lAktPos = myApp.chDB.getPos();
        myApp.lLastID = myApp.lAktID;
        myApp.lAktID = aktZitat.getID();
    } else
        aktZitat = myApp.chDB.getZitat(myApp.lLastID);

    if (aktZitat != null) {
        myApp.lAktID = aktZitat.getID();
        myApp.lAktKategorie = aktZitat.getKategorie();
        myApp.lAktNummer = aktZitat.getNummer();

        tvID.setText("DB-ID: " + String.valueOf(aktZitat.getID()));
        tvNummer.setText("Nummer: " + String.valueOf(aktZitat.getNummer()));
        tvKategorie.setText("Kategorie: " + String.valueOf(aktZitat.getKategorie()));

        doRichtungAnzeigen();

        //damit die Blätter- und Scroll-Gesten nicht kollidieren
        scScroll.setVerticalScrollBarEnabled(false);
        tvAnzeige.setText(aktZitat.getSpruch().replace("\n", " "));
        if (tvAnzeige.getLineCount() >= 14)
            scScroll.setVerticalScrollBarEnabled(true);

        tvSelected.setText("Spruch: " +
                String.valueOf(myApp.lAktPos + 1) + " / " +
                String.valueOf(myApp.lAktMax));
    }
    doCountDown();
}

@Override
public void onResume() {
    super.onResume();
    recallTimer();      //Warum auch immer der Timer aus gegangen war?
}

@Override
public void onGesturePerformed(GestureOverlayView gov, Gesture geste) {
    ArrayList<Prediction> predictions = gestureLib.recognize(geste);
    String sName = predictions.get(0).name;

    //     Vorrang
    //     if (!myApp.bManuell)
    //          if (myApp.bAutomatik)
    //              Abfragen des Timers
    //
    //      bManuell: Schalter gedrückt oder nicht.
    //      + wird durch den Schalter/im ONClick bzw. in den Einstellungen geändert
    //
    //      bAutomatik: Timer wird abgefragt oder nicht
    //      + Änderung durch Gesten (Wischen), aber nur wenn bManuell AUS ist, möglich
    //      + wird (vorsorglich) immer EIN gesetzt, wenn bManuell auf AUS geht im ONClick

    if (!myApp.bManuell) {
        //Wischen nach Links:
        //wenn Auto, dann "anhalten"
        //wenn nicht Auto, dann zeige den letzten Spruch
        if (sName.equals("swlinks")) {
            if (myApp.bAutomatik) {
                myApp.bErstesBlaettern = true;
                myApp.lZeigePos = 1;
                myApp.bVor = !myApp.bVor;
                myApp.bAutomatik = false;
                //Der Thread wird nicht wirklich angehalten, nur das Blättern ausgesetzt
                //pauseTimer();
            } else {
                doNochmal();
            }

            //Wischen nach Rechts:
            //wenn Auto: dann wechsele Vor-/Rückwärts
            //wenn nicht Auto, dann schalte Auto wieder ein
        } else if (sName.equals("swrechts")) {
            if (myApp.bAutomatik) {
                myApp.bVor = !myApp.bVor;
            } else {
                //falls die Automatik aus war, ist noch die Richtung temporär umgekehrt
                myApp.bVor = !myApp.bVor;
                myApp.lZeigePos = 1;
                myApp.bAutomatik = true;
                //Der Thread war nicht wirklich angehalten, nur das Blättern ausgesetzt
                //recallTimer();
            }
        }
    }
    //Manuell (Thread ignoriert)
    else {
        //Wischen nach Links:
        //manuell zurück in der Liste
        if (sName.equals("swlinks")) {
            myApp.bVor = false;
            doNochmal();

        //Wischen nach Rechts:
        //manuell vor in der Liste
        } else if (sName.equals("swrechts")) {
            myApp.bVor = true;
            doNochmal();
        }
    }
    doRichtungAnzeigen();
}

private void doBlaettern() {
    if (myApp.lZeigePos <= myApp.lZeigeMax) {
        anzeigeSpruch(true);
    } else
        doCountDown();
}

private void doNochmal() {
    //der letzte Spruch bei "zufälliger" Auswahl
    if (myApp.bErstesBlaettern && !myApp.bLaufend)
        anzeigeSpruch(false);
    else
        anzeigeSpruch(true);
    myApp.bErstesBlaettern = false;
}

private void doRichtungAnzeigen() {
    if (myApp.bLaufend)
        if (myApp.bVor)
            tvRichtung.setText("vorwärts");
        else
            tvRichtung.setText("rückwärts");
    else
        tvRichtung.setText("zufällig");
    if (myApp.bManuell) {
        if (myApp.bAutomatik)
            tvAutomat.setText("Manuell");
        else
            tvAutomat.setText("");
    }
    else {
        if (myApp.bAutomatik)
            tvAutomat.setText("Automat");
        else
            tvAutomat.setText("Wartend");
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.ibManuell:
            myApp.bManuell = !myApp.bManuell;

            if (myApp.bManuell) {
                if (myApp.iZeit < 1)
                    myApp.iZeitAuto = 1;
                else
                    myApp.iZeitAuto = myApp.iZeit;
                myApp.iZeit = 0;
                myApp.bErstesBlaettern = true;
                myApp.bAutomatik = true;
                myApp.lZeigePos = 1;
            } else {
                myApp.iZeit = myApp.iZeitAuto;
                myApp.bAutomatik = true;
                myApp.lZeigePos = 1;
            }

            doRichtungAnzeigen();

            myApp.iDauer = myApp.iZeit * 1000 + 555;
            thWrk.iDauer = myApp.iDauer;

            break;
        default:
            break;
    }
}

}

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

13.12.2017, 07:47:49 via App

Prüfe mal ob du bei anzeigeSpruch im UI Thread bist. Am besten einfach runInUIThread aufrufen.
Zudem muss ist sagen dein Code ist wenig strukturiert, so können Fremde wie wir diesen nur schlecht lesen...

— geändert am 13.12.2017, 09:30:47

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
swa00
  • Forum-Beiträge: 3.704

13.12.2017, 09:08:17 via Website

@Moin Pascal

Schalte bitte mal die Autokorrektur ab - danke :-)

Liebe Grüße - Stefan
[ App - Entwicklung ]

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

13.12.2017, 12:37:29 via App

Was war denn daran nicht verständlich bis auf meinen Rechtschreibfehler den ich berichtigt habe?
Zudem ist die Autokorrektur schon lange abgeschaltet und nur noch die Wortvorschläge aktiviert...

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Bernd Schubert
  • Forum-Beiträge: 4

13.12.2017, 16:27:57 via Website

Hallo Pascal, er meint wohl ein O statt I. Egal, jedenfalls war es das. Habe runOnUIThread um die SetText herum gemacht und schon lief es nicht nur manchmal sonder immer :-) Nebenbei habe ich diese Activity
ein wenig umstrukturiert (die Kritik an der Reihenfolge und vor allem wohl an dem Durcheinander bei der Gestenbehandlung war berechtigt) und es ist wieder klarer.
Wenn Du willst, kann ich die App, also den Code, auf meine Homepage stellen.
Und auch der Tip mit den Logs war gut, so habe ich noch herausgefunden, daß mein Thread gar nicht sauber "tot" war, sondern noch weiterlief, was aber kein Logikproblem brachte, weil es durch die Anzeigeverzögerung durch das fehlende runOnUIThread verdeckt wurde. Jedenfalls habe ich es gleich noch
so hinbekommen, daß es auch nach Back-Taste und Home-Taste irgendwie weitergehen kann.
Nochmals danke.image

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

13.12.2017, 16:36:04 via App

Ist dein Problem somit gelöst?

LG Pascal //It's not a bug, it's a feature. :) ;)

Bernd Schubert

Antworten
Bernd Schubert
  • Forum-Beiträge: 4

13.12.2017, 16:41:16 via Website

Pascal P.

Ist dein Problem somit gelöst?

Ja, läuft (wieder) :)

Antworten