Aus Hintergrundthread UI-Thread anpassen

  • Hallo,


    ich mal wieder. ;)


    Ich möchte gern im Hintergrund Daten herunterladen (genau genommen muss ich das tun... +knurr+).
    Während des Herunterladens der Daten soll ein AlertView schön den Fortschritt des Downloads anzeigen.
    (Das können 20 oder mehr MB sein, weshalb so ein Fortschrittsbalken definitiv eine gute Idee ist.)


    Ich habe es aktuell so gelöst, dass ich mir eine eigene Klasse DownloadManager gebastelt habe.
    Dieser bekommt eine Instanz des Interfaces DownloadManagerDelegate mit und an den kritischen Punkten sendet er seinem Delegate ein paar Informationen.
    Die kritischen Punkte wären die Folgenden im Interface definierten Punkte:

    Java
    public interface DownloadManagerDelegate {
        public void managerDidBeginDownload(DownloadManager downloader, long expectedBytes);
        public void managerDidReceiveData(DownloadManager downloader, byte[] data, long downloadedBytes, long expectedBytes);
        public void managerDidFinishDownload(DownloadManager downloader, long downloadedBytes);
        public void managerDidFailWithError(DownloadManager downloader, Error downloadError);
    }


    Nun ja, aus irgendwelchen Gründen befindet sich meine als Delegate übergebene Activity nicht im UI-Thread, so dass ich beispielsweise in der Implementierung der managerDidReceiveData(DownloadManager downloader, byte[] data, long downloadedBytes, long expectedBytes); in etwa wie folgt arbeite:


    Wo ich das so einfüge sehe ich, dass ich vielleicht mit dem HolderPattern noch etwas Performance rausholen kann.
    Wie dem auch sei, mir ist das alles zu langsam. Der Fortschrittbalken bleibt signifikant lange bei einem Wert, sagen wir '16%' stehen und hüpft dann quasi zu '53%'. Das sieht doof aus, zumal die Delegate-Methode alle 1024 Byte aufgerufen wird. Irgendwo hakt bei mir also die Performance.


    Wie ist das unter Android übliche Vorgehen, das UI gemäß Daten aus einem anderen Thread anzupassen?
    Mein von iOS bekannter Ansatz des 'Delegating' läuft hier offenbar träge bis gar nicht.
    (Das 'Delegating' unter Mac OS / iOS bedient mindestens das Command Muster.)

    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!«

  • Warum nutzt du keinen AsyncTask ??? Dafür sind die doch gebaut worden.


    http://developer.android.com/r…android/os/AsyncTask.html
    http://www.vogella.com/article…dPerformance/article.html



    Da kannst du deinen Progress einfach schön in onProgressUpdate packen.
    Und musst nicht rum zaubern wegen Handlern und Threads.

  • Warum nutzt du keinen AsyncTask ??? Dafür sind die doch gebaut worden.


    Weil ich just in diesem Moment von deren Existenz erfahre. ;)
    Mein Büchlein[1] schweigt sich darüber aus.


    Dann bastel ich mal, danke für den Tipp. :)


    (Erst mal soll es funktional sein, Code Cleaning kommt dann im Anschluss, wenn ich mich mit dem Thema einigermaßen auskenne.
    Und ich finde, dass ich in den letzten zwei Monaten doch ausreichend gelernt habe, um jetzt damit anfangen zu können.)


    --
    [1] Thomas Künneth: Android 3 Apps entwickeln mit dem Android SDK, Galileo Computing, 1. Auflage 2011

    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!«

    Einmal editiert, zuletzt von Lucas de Vil ()

  • Noch ein kleiner Tipp.


    Du hast wie gesagt in dem onPostexecute und onProgressUpdate Zugriff auf den UI Thread,
    wichtig ist es hier beim Zugriff auf UI Elemente immer gegen null zu testen da du Dir nie sicher sein kannst, das der User die Activity nicht bereits beendet hat. Somit beugst du merkwürdigen Nullpointer Exceptions vor.


    if (textview != null) {
    schreibe meinenporgress in Zahlen;
    }



    Zitat


    Thomas Künneth: Android 3 Apps entwickeln mit dem Android SDK, Galileo Computing, 1. Auflage 2011

    Stimmt ich habe das Buch auch hier, der redet nur von Threads und den Rest lässt er unter den Tisch fallen. :(
    sehr unerfreulich.

  • [killphil]
    Danke für den Tipp mit den Views.
    Ich werde vermutlich dort mit dem ViewHolder arbeiten, da teste ich sowieso immer vorher, ob die Views noch existieren.
    Erschwerend hinzu kommt, dass diese Activity während des Downloads nicht verlassen werden kann. ;)


    Stimmt ich habe das Buch auch hier, der redet nur von Threads und den Rest lässt er unter den Tisch fallen. :(
    sehr unerfreulich.


    Insgesamt habe ich das Gefühl, dass der Herr Künneth für Android so ähnlich ist wie der Herr Hinzberger für iOS. Hilfreiche Dinge auslassen, mit Implementierungsdetails verwirren und schlimmstenfalls noch Falsches einbauen. +sigh+


    ich denke der asynctask ist hier in dem fall auch die beste wahl, meiner meinung nach auch nicht so "anspruchsvoll" wie die ios variante ^^


    Der Vorteil bei der iOS Variante unter iOS ist einfach, dass du stumpf alles komplett getrennt hältst.
    Jetzt habe ich noch locker zwei zusätzliche private Klassen in meiner .java rumfliegen, so dass die Activity selbst unübersichtlicher wird.
    Von den gefühlt 30 Imports reden wir gar nicht erst. ;)


    Insgesamt finde ich die strukturelle Trennung in Java recht ungünstig gelöst. Das ViewHolder Pattern gibt es in iOS Beispielsweise gar nicht, weil der Controller (ein Objekt) Referenzen zur Speicheradresse aller wichtigen Views aus der View-Hierarchie (ein Objektgraph) hat.
    Man könnte stilistisch behaupten, mein findViewById() würde entweder direkt zur Compilezeit fehlschlagen oder niemals. Das ist ja bei Activities nicht so gegeben... ;)


    Insofern ist das Delegating dort State-of-the-Art. Die Dereferenzierung des Delegateobjektes und anschließend der einzelnen Views passiert nahezu in notime.
    Der Hauptgrund, warum ich es erst versuche (ist ein bekanntes Anwendungsmuster) und dann immer auf die Nase falle. (auf Grund der gegensätzlichen Implementierung von Android läuft das hier nicht.)


    [Nur ein bissl blabla, falls mal wer auf die Idee kommt für iOS entwickeln zu wollen. Genauso, wie mir mein iOS Wissen hier nix bringt wird das mit dem Android-Wissen dort sein.]

    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!«

  • Dafür mache ich jetzt aber keinen neuen Thread auf...


    Also meine DownloadAsyncTask().execute(URL... params); erwartet offenbar eine Liste von mehreren URL.
    Jetzt baue ich mir diese mehreren URL an Hand eines Keys aus einem Set zusammen.


    Wie bekomme ich dann jetzt diese mehreren URL als Parameterliste übergeben?


    Sagen wir der Einfachheit halber:

    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!«

  • Einfach als Parameter übergeben. Je nach dem wie dein Konstruktor vom AsyncTask ausschaut.


    new DownloadAsyncTask().execute( fileUrls);



    ----


    protected doInBackground(ArrayList<URL>... passing) {
    ArrayList<URL> passed = passing[0]; //get passed arraylist


    //Some calculations...


    return result; //return result
    }




    Noch mal ein Beispiel, wo sich die Übergabedefinition auswirkt...a



Jetzt mitmachen!

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