Schlagwort gesucht: activity-übergreifende Aktionen mit Zugriff auf den UI Thread...

  • Moin,


    irgendwie treiben mich Androids Activities nach wie vor in den Wahnsinn.
    Deshalb frage ich hier mal nach. :)


    Ich möchte gern Dinge aus dem Internet herunterladen, was an und für sich kein Problem ist.
    Aktuell wird das über den Splash Screen geregelt und ein aufpoppender AlertDialog teilt auch unmissverständlich mit, dass gerade gewartet werden muss.
    Ungeilerweise können ja beim Download durchaus Probleme auftreten, aktuell: der Server antwortet nicht. In diesen Fällen dauert eine Reaktion ewig, weshalb eine blockierende Activity, wie ich sie jetzt habe, nicht in Frage kommt.
    (3x eine Datei von je einem Server laden = 3*3 Minuten Timeout)


    Nun würde ich das Ganze gern auslagern. Meinetwegen in ein Objekt packen und dann an einen Thread übergeben. Nur müsste dieser Thread mitten drin mal ein paar UI Elemente (Dialogfelder) anzeigen. Was er ja nun mal designbedingt nicht kann.
    Erschwerend hinzu kommt, dass ich nicht abschätzen kann, auf welcher Activity sich meine App gerade befindet.
    Die Methoden für AsyncTask einbauen wird also mutmaßlich auch nicht allzu viel helfen, da die Activities ja lustig wechseln können.


    Am Liebsten wäre mir, ich könnte dem Objekt sagen: "Wenn du damit fertig bist, dann starte Activity XYZ auf dem UI Thread und gib ihr die Extras ABC mit."
    Nur: wie geht das?


    --


    Ach ja, ich frage absichtlich nicht nach Möglichkeiten, wie ich die Erreichbarkeit des Servers testen kann.
    Wenn ich keine Daten habe, dann habe ich keine Daten und es interessiert mich nicht, warum ich keine Daten habe.
    Frage ich beispielsweise ab, ob der Server auf einen Ping reagiert, aber bei HTTP kommt kein Response, bin ich genauso weit wie jetzt - nur mit noch mehr Code. Da ich nicht alles berücksichtigen kann (Ping und HTTP geht, aber ich bekomme ne 503 als HTML Seite ausgeliefert...) und dementsprechend auch nicht will möchte ich lieber abstrakt und minimalisiert da ran gehen: entweder ich bekomme gültige Daten oder nicht.
    Wie dabei das 'oder nicht' aussieht interessiert mich erst einmal nicht.

    Je mehr Käse, desto mehr Löcher.
    Je mehr Löcher, desto weniger Käse.
    Daraus folgt: je mehr Käse, desto weniger Käse.


    »Dies ist ein Forum. Schreibt Eure Fragen in das Forum, nicht per PN!«

  • Hoi,


    dein Problem müsste sich eigentlich mit Services lösen lassen. Die kann man einfach anstoßen und die rödeln im Hintergrund vor sich hin. Dem ist egal wo genau du gerade in deiner App bist. Mittels Handler kann der Service mit Activitys kommunizieren und Messages schicken, auf die du dann von der Activity aus reagieren kannst. Das ganze kann man auch mit einem BroadcastReceiver verknüpfen.


    Allerdings habe ich das ganze noch nie selbst getestet, da ich bisher keinen Anwendungsfall dafür hatte. Aber Lars Vogel hat in seinem Blog ein nettes Tutorial dazu geschrieben -> zum Tutorial.


    Vielleicht hilft dir das ja weiter ;)


    Gruß,
    matze

  • Hallo Lucas,


    da gibt es verschiedene Ansätze. Du könnest deine App auch umbauen auf Fragmente und mit nur einer Activity arbeiten aber da währe der Aufwand wohl zu gross.


    Ich würde das mit einem Downloader realisieren der als Singleton gebaut ist, einen Download Thread enthält und dem man einen Listener für Progressupdate etc setzen kann. Dann wird immer der aktuelle Listener updatet und du kannst dich beim Activity wechsel wenn gewünscht als Listener registrieren. Hatte gerade Lust und hab dir was aus meinen Codes rausgesucht, hilft dir vielleicht :) Ist halt auf die Basics zusammengekürzt, sonst einfach fragen...


    Der Downloader und eine Activity findest du im Anhang.


    Gruss
    antifish

  • Hat sich noch ein Fehler eingeschlichen,
    Zeile 34 im SingletonDownloader müsste natürlich mThread.start(); sein


  • Herzlichen Dank für Eure Hilfe, Ihr beiden. :)


    [matthias]
    Die Sache mit den Services sieht spannend aus. Das werde ich mir mal näher zu Gemüte führen. Eventuell kann ich davon was gebrauchen, auf für zukünftige Dinge.


    [antifish]
    Danke für den Code!
    Nach wie vor habe ich dann aber schlimmstenfalls pro Activity die Listener implementiert.
    Irgendwie halte ich ein eigenes Fragment für oversized, da in diesem speziellen Fall der Download nur einmal beim Start (onCreate) angestoßen und dann bei blockiertem UI weiterrattern soll.
    Mal sehen, wie ich das noch in den Griff bekomme. :)


    Zunächst habe ich das 'Blockieren'-Problem mit einem extrem kleinen Timeout-Wert für die URL Connection umgangen.
    Verdammte persönlich gesetzte Deadlines. ^^

    Je mehr Käse, desto mehr Löcher.
    Je mehr Löcher, desto weniger Käse.
    Daraus folgt: je mehr Käse, desto weniger Käse.


    »Dies ist ein Forum. Schreibt Eure Fragen in das Forum, nicht per PN!«

  • Hallo Lucas,


    okay vielleicht hab ich auch dein Problem noch nicht ganz verstanden :)
    Für welche Android Version entwickest du?
    Seit denn neuern Versionen ist es ohne Umweg sowieso nicht mehr möglich Internet / Netzwerk Funktionen im GUI Thread aufzurufen ohne dass die App abstürzt (NetworkOnMainThreadException).
    Also eigentlich dürfte dein GUI nicht blockieren, ausser du meinst mit blockiern was anderes?
    Wenn du dein Download Code in den SingletonDownloader in die run() Methode packst dann blockiert am GUI überhaupt nix!


    Du kannst dir sonst mal die (sexistische 8) ) Sexy Jigsaw App von mir anschauen, die lädt im Hintergrund auch Bilder herunter, und es ist egal wie lange das dauert, ob Minuten oder Stunden, die App läuft flüssig weiter.


    Gruss
    antifish

  • Ich ahnte schon, dass ich bezüglich der sexistischen Kackscheiße noch einen Kommentar ernten werde. ;)


    Vermutlich funktioniert dein Puzzle wie folgt: du hast bereits x Bilder im Bundle oder auf dem Dateisystem. Während deine App läuft, lädt sie die Bilder im Hintergrund herunter und packt sie dann ins Dateisystem. Solange die Bilder nicht da sind, werden sie deinem Spiel auch nicht als neues Bild angeboten und es sieht auch niemand einen "Bild <Vorschau> steht jetzt zum Puzzlen bereit!" Dialog.


    In meinem Fall ist das ein bisschen anders. Würde ich die heruntergeladenen Daten sequenziell ablegen, würde der Speicherplatz explodieren.


    Man könnte sagen, die Anwendung soll diverse RSS-Feeds von diversen Servern ziehen - inklusive Bilder, Videos, Musik und was da noch alles dran hängt.
    Diese Daten können an Aktualität verlieren, weshalb ich alte Informationen löschen und nur die neuen Informationen ablegen möchte.


    Um das alles möglichst ressourcenschonend zu gestalten, habe ich mir folgende Abläufe überlegt:
    - der Download der RSS/XML wird nur ein einziges mal gestartet, nämlich in der OnCreate der Launch-Activity (eine Art SplashScreen)
    - dann wird eine ID mit der gespeicherten letzten ID verglichen
    - sind die IDs unterschiedlich, werden die Inhalte dieser XML zum Aktualisieren vorgemerkt
    (Das Ganze passiert aktuell 1 bis 12 mal für 1 bis 12 Dateien auf 1 bis 12 Servern mit je 1 bis 12 MB Dateigesamtgröße)
    - dem User werden die zum Aktualisieren vorgemerkten Informationen gezeigt und er wählt via Checkbox einzelne Aktualisierungen an und ab
    - anhand dieser Auswahl werden die vorhandenen Detailinformationen vom Dateisystem gelöscht
    - anschließend werden die Detailinformationen gemäß der Auswahl heruntergeladen und in das Dateisystem gepackt (Pre-Rendered HTML, Bilder, Sounds, Videos...)


    Im ersten Schritt darf das UI nicht blockieren, da hier viel schief gehen kann. Doch die Anzeige der Informationen soll am Ende aller Downloads erfolgen, unabhängig der Activity.
    Ab diesem Punkt können mir viel zu viele Unbekannte passieren:
    - das ListView wird aus einer SQLite Datenbank generiert (da XML parsen arschlahm sein kann), welche mitten drin gelöscht und neu bespielt worden sein kann
    - die Detailansicht verzweigt auf die Daten im Dateisystem, welche gerade gelöscht, verändert oder sonstwie manipuliert worden sein können


    Das alles und noch viel mehr kann zu unvorhersehbarem Verhalten der Anwendung führen.
    Deshalb möchte ich, dass sobald die ersten Informationen heruntergeladen wurden, ein AlertDialog zunächst den Download der vielen größeren Dateien visualisiert (natürlich abbrechbar) und anschließend sämtliche Änderungen an der Datenbank und dem Dateisystem mittels Kreisel darstellt.
    Der User sieht also, dass er diese App gerade nicht benutzen kann (anstatt sie zu benutzen und feststellen zu müssen, dass gerade irgendwas schief ging).


    ---


    Mit dem Blockieren der UI meinte ich, dass meine SplashScreen Activity gezeigt wird solange die RSS/XML Datei heruntergeladen wird. Im Allgemeinen ist das wurscht, da die Files echt winzig sind. Im Speziellen (Server nicht erreichbar) zeigte sich dieser Splashscreen allerdings 10 Minuten. Das soll so nicht sein.


    Also eigentlich suche ich nach wie vor nur nach einer Möglichkeit, nach erfolgreichem Download aller RSS/XML Dateien im Hintergrund einen Dialog anzuzeigen - unabhängig der aktuellen Activity. +sigh+


    Nun, vielleicht bleibt die aufrufende Activity aber auch so lange im Speicher, dass ich dort einfach den UIThread als Instanzvariable mitgeben kann und dem Ding dann die gewünschten Methoden zuposte. Mal herumprobieren.

    Je mehr Käse, desto mehr Löcher.
    Je mehr Löcher, desto weniger Käse.
    Daraus folgt: je mehr Käse, desto weniger Käse.


    »Dies ist ein Forum. Schreibt Eure Fragen in das Forum, nicht per PN!«

  • Dies könntest du immernoch mit dem Ansatz der von mir gezeigtem Code lösen (dem Singleton Downloader).
    Anstatt denn Listener zu setzen müsste dein Downloadthread einfach eine "Activity" zugewiesen bekommen auf welcher er denn Dialog anzeigen kann. Du müsstest dann einfach bei jedem Activity Wechsel dem Downloader die aktuelle Activity mitteilen.


    Also eigentlich suche ich nach wie vor nur nach einer Möglichkeit, nach erfolgreichem Download aller RSS/XML Dateien im Hintergrund einen Dialog anzuzeigen - unabhängig der aktuellen Activity. +sigh+

  • Anstatt denn Listener zu setzen müsste dein Downloadthread einfach eine "Activity" zugewiesen bekommen auf welcher er denn Dialog anzeigen kann. Du müsstest dann einfach bei jedem Activity Wechsel dem Downloader die aktuelle Activity mitteilen.


    Das klingt machbar, danke! :)

    Je mehr Käse, desto mehr Löcher.
    Je mehr Löcher, desto weniger Käse.
    Daraus folgt: je mehr Käse, desto weniger Käse.


    »Dies ist ein Forum. Schreibt Eure Fragen in das Forum, nicht per PN!«

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!