1. Mitglieder surfen ohne Werbung auf Android-Hilfe.de! ✔ Jetzt kostenlos Mitglied in unserer Community werden.
  1. miriki, 02.04.2018 #1
    miriki

    miriki Threadstarter Neuer Benutzer

    Moinsens!

    Nachdem ich hier schon länger lesend im Forum unterwegs bin, hab ich mich jetzt mal angemeldet, um auch mal selbst eine Frage reinstellen zu können:

    Ich versuche gerade, eine eigene Klasse zu erstellen, die von einer textView (genau genommen
    AppCompatTextView) abgeleitet ist. Diese soll sich selbständig (z.Z. per Timer) aktualisieren. Leider geht .setText() nicht aus dem Widget heraus. Es kommt zum Fehler: "android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."

    Also... Ich habe...
    a) Eine "public class MainActivity extends AppCompatActivity" (API 19, soll noch auf meinem alten Note II laufen)
    b) Eine "class MrkTextClockWidget extends AppCompatTextView"

    In a) wird dann die textView erzeugt und gesetzt:
    Code:
    private void createLayoutAndWidgets() {
        LinearLayout ll;
        LayoutParams lp;
        MrkTextClockWidget t;
        ll = new LinearLayout( this );
        ll.setOrientation(LinearLayout.VERTICAL);
        lp = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT );
        ll.setLayoutParams( lp );
        t = new MrkTextClockWidget( myContext );
        lp = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT );
        t.setLayoutParams( lp );
        ll.addView( t );
        setContentView( ll );
    } // createLayoutAndWidgets
    wird vom onCreate() aufgerufen:

    Code:
    @override
    protected void onCreate( Bundle savedInstanceState ) {
        super.onCreate(savedInstanceState);
        myContext = getApplicationContext();
        // displayMessage( "onCreate" );
        createLayoutAndWidgets();
    } // onCreate 
    In b) rufen die 3 Varianten des Constructor dann init() auf:
    Code:
    private void init() {
        Calendar c;
        Date n;
        Timer t;
        TimerTask tt;
        Date ft;
        long p;
        t = new Timer();
        c = Calendar.getInstance();
        n = c.getTime();
        tt = new TimerTask() {
            @override
            public void run() {
                updateWidget();
            }
        };
        ft = n;
        p = 1000;
        t.scheduleAtFixedRate( tt, ft, p );
    } // init
    Und dann die eigentliche Routine, die das Widget aktualisieren soll:
    Code:
    private void updateWidget() {
        Calendar c;
        Date n;
        DateFormat f;
        c = Calendar.getInstance();
        n = c.getTime();
        f = android.text.format.DateFormat.getDateFormat( myContext );
        setText( f.format( n ));
    }
    Tja, aber wie im Betreff und eingangs beschrieben, klappt das so nicht. Und ich krieg einfach die Syntax nicht hin, wie es laufen soll. Im Web finde ich bislang nur Beispiele, wie eine Timer-textView im "main" läuft. Ich brauch das aber zwingend als eigene Klasse (und damit wohl auch eigenen Thread, soweit ich das verstanden habe). Diese textView Geschichte ist nur anfängliche Spielerei. Ich brauche später etwas aufwändigere Widgets, die so selbständig wir nur irgend möglich arbeiten. Ich will eigentlich im "main" nur noch das Widget setzen und ggf. ein paar Properties setzen, mich aber ansonsten nicht mehr drum kümmern müssen.

    Hilft mir hier jemand auf die Sprünge? Ist ja vielleicht echt nur 'ne Kleinigkeit. Aber ich dreh mich hier langsam im Kreis.

    Gruß, Michael
     
    Zuletzt bearbeitet: 02.04.2018
  2. swa00, 02.04.2018 #2
    swa00

    swa00 Moderator Team-Mitglied

    Hallo Michael,

    herzlich willkommen im Forum.

    a) Bitte sei so nett und formatiere deinen Source mit dem Code-Tag - Dann ist er auch besser zu lesen :)
    b) zu deiner Frage : Du musst die Anzeige im UI-Thread ausgeben

    Code:
      runOnUiThread(new Runnable()
            {
                @Override
                public void run()
                {
                   setText (.....
                }
            });
     
  3. miriki, 02.04.2018 #3
    miriki

    miriki Threadstarter Neuer Benutzer

    Das hätte ich gerne gemacht, ich fand den nur nicht. Aber manuell scheint {code} .. {/code} ja zu gehen, wenn ich mir das Zitat von Dir so ansehe. Hinter welchem der Icons in der Leiste über dem Editor versteckt sich "code"?

    Aber vielen Dank erstmal für Deine Antwort!

    Das hab ich schon so verstanden und auch mehrmals in anderen Beiträgen gelesen. Aber es paßte irgendwie nie zu meinem Widget. Ich glaube, das "runOnUiThread" gehört zum Fragment und deswegen kann ich es nicht benutzen. Aber so ganz verstanden habe ich das nicht.

    Wo soll ich denn
    Code:
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            setText (.....
        }
    });
    genau einsetzen? Meine "update"-Routine sieht jetzt aus wie:
    Code:
    private void updateWidget() {
        runOnUiThread( new Runnable() {
            @Override
            public void run() {
                Calendar c;
                Date n;
                DateFormat f;
                c = Calendar.getInstance();
                n = c.getTime();
                f = android.text.format.DateFormat.getDateFormat( myContext );
                setText( f.format( n ) );
            }
        });
    }
    
    Und jetzt krieg ich nur die Meldung in der IDE: "Cannot resolve method 'runOnUiThread( anonymous java.lang.Runnable )'"

    Gruß, Michael
     
    Zuletzt bearbeitet: 03.04.2018
  4. swa00, 03.04.2018 #4
    swa00

    swa00 Moderator Team-Mitglied

  5. miriki, 03.04.2018 #5
    miriki

    miriki Threadstarter Neuer Benutzer

    Naja, aber die wesentlichen Teile doch wohl schon, oder?

    Ah, ok, jetzt kommen wir der Sache näher. Den Context übergebe ich beim Erstellen an das Widget. Dieser wird lokal gespeichert:
    Code:
    class MrkTextClockWidget extends AppCompatTextView {
    
    private Context myContext;
    
    public MrkTextClockWidget( Context context ) {
        super( context );
        myContext = context;
        init();
    } // constructor
    
    [...]
    Gruß, Michael
     
  6. miriki, 04.04.2018 #6
    miriki

    miriki Threadstarter Neuer Benutzer

    Moin, swa00!
    Also ich schrieb ja schon, glaube ich, daß ich auf der Seite auch schon war. Tatsächlich gehört die Seite zu meinen primären Lesezeichen. Aber: Ich sehe / verstehe absolut nicht, wie mir das dort weiterhilft.

    Dort ist jetzt von runOnUIThread oder Context überhaupt nicht mehr die Rede. Dafür geht's mit einem mHandler los, der (bei mir) auch wieder nicht aufgelöst werden kann. Und von dem mHandler hab ich in einigen StackOverflow-Artikeln auch schon was mitbekommen, aber ebenfalls nicht umsetzen können.

    Und ich will ja eigentlich auch nicht eine komplette Thread-Verwaltung über irgendeinen Pool haben. Ich will einfach nur, daß mein Widget sich selbst aktualisiert, ohne daß im "main" irgendwas berücksichtigt werden muß. Ist das denn wirklich so schwierig?

    Gruß, Michael
     
  7. swa00, 04.04.2018 #7
    swa00

    swa00 Moderator Team-Mitglied

    Hallo Michael,

    du hast doch bereits deinen Context in MyContext zugewiesen.

    runOnUIThread ist ein Objekt aus Diesem
    Ergo hast du schlichtweg mal MyContext.runOnUIThread probiert ?


    Und genau das meinte ich Eingangs mit : " Der komplette Code fehlt" die Deklaration von MyContext war nicht bekannt .
    Deshalb können wir auch nur rätseln , wo dein Fehler liegt .

    Ggf. musst du von MyContext eine temporäre final Variabel deklarieren

    So ganz nebenbei : Dein TimerThread ist eher "OldSchool" und wird dir Probleme bereiten.
    Nimm lieber einen ordentlichen AsyncTask in einem separatem Objekt
     
  8. miriki, 05.04.2018 #8
    miriki

    miriki Threadstarter Neuer Benutzer

    Moin, @swa0!
    Jo, hatte ich, mit nach wie vor der genau gleichen Fehlermeldung. Beim myContext hatte ich auch private / public und final / static ausprobiert, aber ebenfalls keine Änderung.

    Wo? Innerhalb des Runnable?

    Ja, auf lange Sicht wird der wahrscheinlich durch irgendwas mit AlarmManager und setExact() ersetzt. Die Widgets sollen später Sensor-Werte darstellen und ggf. Alarm schlagen. Da hilft es mir nicht, wenn Android den ganzen Kram in den Schlafmodus legt. Allerdings muß ich mit der API aufpassen. Wie oben geschrieben: Der Kram soll noch auf dem alten Note II mit API 19 laufen.

    Gruß, Michael
     
  9. markus.tullius, 06.04.2018 #9
    markus.tullius

    markus.tullius Android-Lexikon

    Hallo miriki,

    new Runnable() erzeugt ein anonymes Objekt. An dieser Stelle mag Java das nicht.

    Versuche es mal mit:

    Code:
    Runnable aRunnable = new Runnable() {
        @Override
        public void run() {
            Calendar c;
            Date n;
            DateFormat f;
            c = Calendar.getInstance();
            n = c.getTime();
            f = android.text.format.DateFormat.getDateFormat( myContext );
            setText( f.format( n ) );
        }
    };
    
    runOnUiThread(aRunnable);
    
    
    Denn Code habe ich nicht ausprobiert, denke aber dass die Lösung es das Problem behebt
     
  10. miriki, 07.04.2018 #10
    miriki

    miriki Threadstarter Neuer Benutzer

    Hi, Markus!

    Ich hab meinen Code mal dahingehend geändert. Das brachte leider am eigentlichen Problem noch keine Änderung. Das "runOnUiThread" konnte nach wie vor nicht aufgelöst werden. Nichts desto trotz laß ich das so, wie von Dir vorgeschlagen. Es sieht einfach irgendwie besser aus.

    Das entscheidende Problem aber war:

    Damit hat er mich doch echt auf's Glatteis geführt. ;-)

    runOnUiThread ist eben kein Objekt aus "diesem", sondern aus der Activity. Ich übergebe aber einen Context an den Constructor. Ich brauchte einfach nur den Context auf eine Activity-Variable casten und schon lief's.

    Im init():
    Code:
    myContext = context;
    myActivity = (Activity) myContext;
    Entscheidende Beiträge aus dem Netz dazu:
    How do we use runOnUiThread in Android?
    Getting activity from context in android

    Gruß, Michael
     
  11. swa00, 07.04.2018 #11
    swa00

    swa00 Moderator Team-Mitglied

    Hallo Michael,

    sorry für das "Glatteis" , aber woher sollte ich erkennen ob dein onCreate aus der Activity oder woher auch immer stammt ?



    Und da wir Helfenden i.d.R. Dies schon länger (hauptberuflich) tun:

    Auch Markus hat nicht auf Anhieb erkennen können , wie die Zusammenhänge sind.
    Ich sehe also kein Glatteis , sondern eher eine Antwort auf Teilinformation.

    Sei bitte so lieb und poste beim nächsten Male nicht nur Fragmente, sondern ALLES.
    Ich hatte es schon Eingangs erwähnt und sorgt auch nicht für zahlreiche Nachfragen und Vermutungen

    Viel Erfolg
     
    Zuletzt bearbeitet: 08.04.2018
  12. markus.tullius, 07.04.2018 #12
    markus.tullius

    markus.tullius Android-Lexikon

    Danke für die Rückmeldung. Statt des casten kannst du auch MainActivity.this.runOnUIThread() schreiben. Ob die Lösung von mir elegant ist, darüber kann man streiten. ;)

    Noch ein paar Bemerkungen zu deinen Code:
    1. Variablennamen wie ll, t, n usw. sind nicht das gelbe von Ei. In einen halben Jahr weiß du nicht, was die Abkürzungen bedeuten. Liebe linearLayout o. timer usw. Das macht den Code lesbarer.
    2. Befasse dich mal mit Threads,
    3. So wie der Code da steht, hat deine App ein Speicherleck (Memoryleak). Wenn du ein Timer (einen Thread), muss du ihn auch wieder schließen (timer.cancel(): timer = null) , sonst räumt der GC den Timer nicht aus den Speicher. Und es fehlt eine Fehlerbehebung (try and catch).
    -- Dieser Beitrag wurde automatisch mit dem folgenden Beitrag zusammengeführt --
    PS: Ich glaube das war kein Glatteis, sondern schon der richtige Ansatz. Da ich den Code nicht komplett kenne, aber ich bin mir fast sicher, das der Methodenaufruf setText() der eigentliche Übeltäter ist. Probier mal MainActivity.this.setText(). Würde mich nicht wundern, wenn es dann funktioniert.
     
    swa00 bedankt sich.
  13. miriki, 08.04.2018 #13
    miriki

    miriki Threadstarter Neuer Benutzer

    Hi, Markus!

    Das werd ich heute abend vielleicht noch ausprobieren.

    Solche Murk-Namen benutze ich nur bei "quick and dirty" prototypes und auch dann meist nur in so 5-zeiligen Subs. Globale halte ich schon etwas aussagekräftiger.. In der "Produktiv"-Umgebung bin ich da aber auch insgesamt eine ganze Ecke sauberer.

    Das habe ich unter Delphi, VB.Net, Python und Java eigentlich schon weitestgehend hinter mir. Ich brauch Threads selten, aber wenn, komm ich damit eigentlich schon klar. Aber unter Android sieht die ganze Sache doch immer wieder ganz anders aus. Der ganze Kram um Activity, Context, Fragment, Widget, ... Da sind noch einige Begriffe, die ich vom Verständnis noch nicht so ganz drin habe.

    Ah, guter Hinweis... Daran hab ich jetzt tatsächlich noch gar nicht gedacht. Da muß ich mir auch noch überlegen, _wo_ ich da das passende Aufräumen einbaue.

    Das Glatteis war "Context" statt "Activity". Wenn ich eine "Context"-Variable habe, werde ich darauf eben nie nicht ein runOnUiThread basieren lassen können. Deswegen ja das "cannot resolve" die ganze Zeit. Daß ich aber einfach den Context auf eine "Activity"-Variable casten kann, der Hinweis fehlte.

    Und ich schrieb eingangs, daß ich eine Activity habe, von der im onCreate() / init() ein Widget erstellt wird, welches den Context übergeben bekommt (nicht explizit geschrieben, aber stand so im Code).

    Aber ist ja alles gut, letztendlich hab ich's ja hinbekommen.

    Jetzt muß ich nur noch herausfinden, wieso der MediaPlayer immer nur das 1. mal (Minuten-Chime) funktioniert. Aber das ist eine andere Geschichte... ;-)

    Gruß, Michael
     
  14. markus.tullius, 09.04.2018 #14
    markus.tullius

    markus.tullius Android-Lexikon

    Hi Michael,

    Android setzt massiv auf Threads. Die Activities laufen in Threads, man sieht sie nur nicht. In deinen Fall läuft also ein Thread in einem Thread.

    Um sauberen Code zu produzieren braucht man den Lifecycle. Hier bekommt man die Möglichkeit, den Code aufzuräumen.
     
    swa00 bedankt sich.
  15. swa00, 09.04.2018 #15
    swa00

    swa00 Moderator Team-Mitglied

    Hallo Michael,

    Ich stamme aus der C/C++ Ecke und Du musst bei Android komplett umdenken .
    Das hat auch nichts mehr mit Java ansich zu tun, sondern ist lediglich nur die Umsetzungsform

    Und wie Markus schon treffend bemerkt hat :

    90% der Runtime sollten unter Android inr Threads laufen.
    Aufeinanderfolgende "Spaghettifunktionen" a la Delphi, VB und Phyton wird nicht funktionieren.

    Du musst auch immer bedenken , dass dir Android jederzeit Werte von Variablen verändern
    in den Ablauf eingreifen und auch das gesamte Programm beenden kann.

    Deshalb auch besonders ein Auge auf Lifecycle, GC und Listener/Callbacks & Threads werfen.

    Auch hier vermute ich stark , dass du das Ganze nicht asynchron eingepflegt hast.
     
    Zuletzt bearbeitet: 09.04.2018
Die Seite wird geladen...
Ähnliche Themen Forum Datum
Message an inaktiven Main-Thread Android App Entwicklung 17.07.2018
Thread von außen stoppen Android App Entwicklung 07.07.2018
TextView wert übergeben Android App Entwicklung 28.06.2018
Fragment Textview Text von non-Activity ändern Android App Entwicklung 27.01.2018
[OFFEN] The application may be doing too much work on its main thread Android App Entwicklung 01.11.2017
Du betrachtest das Thema "custom textView .setText() - UI thread" im Forum "Android App Entwicklung",
  1. Android-Hilfe.de verwendet Cookies um Inhalte zu personalisieren und dir den bestmöglichen Service zu gewährleisten. Wenn du auf der Seite weitersurfst stimmst du der Cookie-Nutzung zu.  Ich stimme zu.