Gravierende Verständnisprobleme Async Tasks

  • Moin,


    ich muss mal wieder nerven. ;)


    Also ich verstehe das Konzept der Asynchronen Tasks wie folgt:
    - Es gibt Dinge, die auf dem UI Thread durchgeführt werden. Namentlich onPreExecute, onPostExecute und onProgressUpdate.
    - Es gibt Dinge, die in einem anderen Thread durchgeführt werden. Namentlich doInBackground.


    Das ist zumindest das, was mir die Dokumentation erzählen möchte.


    Jetzt stelle ich fest, dass dem eben nicht so ist. onPreExecute, onPostExecute und onProgressUpdate werden auf dem Thread ausgeführt, auf dem die Java-Klasse läuft. Dies muss nicht zwangsläufig der UI Thread sein und ist es in meinem Fall auch überhaupt nicht - irgendwie.


    Also Folgendes: ich habe eine Klasse, die im Hintergrund ein paar Daten herunterladen soll.
    Der Hintergrunddatenherunterladevorgang wird in einem asynchronen Task dieser Klasse durchgeführt.
    Die onPreExecute, onPostExecute und onProgressUpdate haben keinen korrekten Zugriff auf den UI Thread.

    Zitat

    Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


    Nun wollte ich erstmal ein paar Arbeiten vorverlegen. Ich habe mir eine Art Notification Center erstellt (wenn es dafür etwas Fertiges gibt, immer her mit der Info).
    Nicht das Ding im OS, welches irgendwelche Informationen ausspuckt. Eher eine Art Beobachter/eigener Listener für alles Mögliche.


    Nun stelle ich leicht genervt fest:


    Tja, was soll ich sagen...

    Zitat

    Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


    Meine 'anonymous inner class' von NotificationCallback() ist also auch irgendwo, nur nicht auf dem UI Thread.


    Weiter unten in der Dokumentation finde ich noch den Hinweis, dass AsyncTask doch bitte nur über den UI Thread aufzurufen sei. Also sämtliche Überlegungen umsonst.


    Wie bekomme ich denn jetzt den Download in einem anderen Thread durchgeführt und die Daten wie 'Aktuell heruntergeladen' und 'Gesamt' an den UI Thread überführt, wenn der Download etc.pp. von einer eigenen Klasse aus durchgeführt werden soll?

    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,


    mit der AsyncTask hab ich auch schon einiges an Schwierigkeiten gehabt, ich versuche immer, die Inhalte von onPostExecute so kurz wie möglich zu halten und die Aufgaben als Methoden ausserhalb der Task unterzubringen.


    Der Dialog, den du dort aufbaust, ist so weit erst mal ok, aber wenn das in der Task steht, ist es immer noch in der Task und nicht im UI - und da müssen Dialoge gebaut werden.


    Leider fehlt auch, was du in der postExecute machst - das würde mir etwas mehr helfen... ;)


    LG
    Uwe

  • Hi Uwe,


    freut mich zu lesen, dass ich nicht der Einzige mit Verständnisproblemen bin. :)


    In der postExecute() mache ich aktuell:

    Java
    @Override
    // Also executed on the UI Thread, they said.
    protected void onPostExecute(Long result)
    {
        Log.e(TAG, "Download completed with result "+result);
    }


    Gezeigter Codeschnippsel ist der Bereich, der mir die erste Exception wirft: ich habe also in meiner anonymen Inner-Class keinen Zugriff mehr auf meinen UI Thread, was das Ganze extrem erschwert.
    Der ganze Threading Kram wird erst bei daConnector.loadDataUpdatesForTypes(types); angeschmissen, und zwar in der Connector Klasse.


    Das Ganze ist noch Testcode, weshalb es kein ordentliches Exceptionhandling gibt.


    Meine Idee war ein eigener Download-Manager, der einfach unabhängig von wo auch immer aufgerufen ein paar entsprechende Dialoge darstellt.
    Die Trennung der Logik (also Download über mehrere Threads) von der Darstellung (Download-Dialog beispielsweise mit Fortschrittsanzeige) und anschließendes Zusammengeleime fällt mir hier arg schwer.


    Nur nützt es rein gar nichts, irgendwelchen im Android SDK nicht existenten Lösungen herzutrauern. Es muss sich irgendwie umsetzen lassen. Meine Frage ist nur: wie.


    Den UI Handler möchte ich halt ungern immer mitschleppen und alles darauf auslagern. Aber offenbar muss ich das tun.

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

  • Ach ja, der interne Download-Manager wäre vielleicht eine Option, wenn man ihm erklären könnte, dass er
    entweder) die Download Notifications sein lässt
    oder) alle 1 bis 5 Dateien als eine einzige Notification anzeigt


    Aber eigentlich finde ich das ungeil. :-/

    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,


    du schreibst:


    Zitat

    Meine Idee war ein eigener Download-Manager, der einfach unabhängig von wo auch immer aufgerufen ein paar entsprechende Dialoge darstellt.


    Genau dabei hatte ich auch Probleme und hab deshalb meine AsyncTask als Subklasse an verschiedenen Stellen eingebaut. Dadurch konnte ich auf Variablen und Methoden der umgebenden Klasse zugreifen.


    Wenn du die Task aber als eigene Klasse schreibst, könnte dir ein Interface helfen, das die UI-KIassen implementieren müssen und über das du in den pre/postExecute die UI-Implementation aufrufst.


    Das Erzeugen von Dialogen wirst du wohl leider in den UI-Klassen anstoßen müssen, das können aber wieder eigene Klassen sein, damit du das MVC einigermaßen sortiert kriegst, außer jemand hat noch eine bessere Idee... *grübel*


    Zitat

    entweder) die Download Notifications sein lässt
    oder) alle 1 bis 5 Dateien als eine einzige Notification anzeigt


    Wenn die Task mehrfach gestartet wird, gibt's auch mehrere postExecutes - ich hab in meinem Code versucht, dies zu vermeiden.


    Ist ja auch blöd, wenn der User mehrere Dialoge um die Ohren gehauen bekommt. Eigentlich könnte die AsyncTask im doInBackground am Ende die Ergebnisliste zusammensammeln und dann hast du nur einen postExecute - eigentlich...

  • +hm+
    Also ist mein Vorhaben nicht trivial?


    Gut, dann gebe ich einfach den Handler des UI Thread mit und gut ist.
    Der bekommt dann die Erstellung der Dialoge aufgedrückt.


    Wegen der Vermeidung schaue ich mal. Da ich es irgendwie eh nicht schaffe, aus einem Array eine Parameterliste zu basteln, werden es trotz mehrere Downloads offenbar dennoch nicht mehrere postExecutes werden.


    Eigenartig, dass dieses Thema so komplex anmutet.

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

  • Puh - Server läuft wieder. *freu* Dann kann ich meine Antwort ja endlich loswerden...


    Der Task einen Handler auf die UI mitzugeben (im Konstruktor) ist auf jeden Fall auch ok, finde ich. 8)


    Nur weiß ich gerade nicht, was du mit "einem Array eine Parameterliste zu basteln" meinst. So etwas wie toArray()??


    Du könntest ja auch gerne den AsyncTask als <Url, Long, long> definieren und in der Schleife öfter aufrufen, aber dann hast du auf jeden Fall mehere postExecutes.


    Aber genau das sollte auch funktionieren wenn du das Array einmal mit execute übergibst und dann die Parameter abarbeitest.


    Ich habs zwar nicht probiert, aber wenn du AsyncTask<Url, Long, Long> definierst, sollte ein execute(urlArray) dir auch die Parameter als Array liefern, so wie du sie auch schon verarbeitest, aber eben nicht Listen, sondern nur Url... als Paramter.


    Es geht natürlich auch AsyncTask<Object, Long, Long> und dann execute(context, urlList) - dann mutig casten und gucken, ob du die Log-Ausgaben siehst.


    Hmm - bin selber wieder etwas verwirrt, könnte auch mit der kleinen Erkältung zusammenhängen, die ich gerade ausbrüte. *schnief*


    Vielleicht waren bei dem Geschreibsel hier ja ein paar Gedanken dabei, die dir weiterhelfen - ich weiß gerade auch nicht weiter, ohne dass ich größere Aktionen hier starte und Code implementiere... :*

  • Ich habs zwar nicht probiert, aber wenn du AsyncTask<Url, Long, Long> definierst, sollte ein execute(urlArray) dir auch die Parameter als Array liefern, so wie du sie auch schon verarbeitest, aber eben nicht Listen, sondern nur Url... als Paramter.


    Ahaa.

    Java
    URL[] urls = new URL[urlArray.size()];
    new Downloader().execute(urlArray.toArray(urls));


    So läuft es auch ohne seltsame Warnungen. :)
    Ich muss nur mal schauen, ob ich jetzt nicht doch zufällig zu viele OnPostExecutes bekomme...
    Nö, bekomme ich nicht. Na, damit kann man doch arbeiten. :)


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

  • Ich würde vielleicht noch die Liste urlArray umbenennen in urlListe - dann liest es sich besser. *klugscheiß*


    Ist aber ne ArrayList. Also müsste es höchstens urlArrayList heißen. :P

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

  • Bald nutze ich nur noch Collection. Diese Möglichkeiten der Unterteilung sind ja echt bekloppt. 8|


    Wie dem auch sei, es ist eine ArrayList.
    Ich kann sie ja prima als solche casten und der Name verrät mir schon, dass ich keine ClassCastException dabei bekommen werde. :P
    (Dinge, die man beim NDK und Objective-C lernt: Ein Objekt ist immer das Objekt, das man erstellt hat. Egal wie man es zu Anfang deklariert und egal wohin man es zu casten versucht.)

    Java
    Object id = new Date();


    Was ist id denn jetzt, ein Object oder ein Date?
    Die Runtime lässt mich nur die Object-Methoden auf id ausführen, ich muss also erst einmal casten.
    Doch ich kann id auf Date casten, weil es ein Date ist.
    Will ich id auf String casten, bekomme ich eine ClassCastException. id ist ja schließlich kein String.


    Im NDK ist das übrigens komplett anders.
    Da kann ich lustig jLongArray in jString in jDate in jDouble casten. Alle Objekte im ndk sind als 'void *' definiert, wie auch alle Objekte in Objective-C.
    Folgendes funktioniert da tatsächlich und wirft keine Exceptions, Fehler oder Warnungen:

    C
    jdouble einspunktdrei = env->newDate();
    (jLongArray)einspunktdrei;
    (jString)einspunktdrei;
    jdate today = env->newString("Heute");
    (jArrayList)today;


    Dennoch ist einspunktdrei ein Datum und today ein String.

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

    2 Mal editiert, zuletzt von Lucas de Vil ()

  • Lucas du hast den Bedankenbutton vergessen :)


    Nachgereicht. ^^

    Hab schon auf die Antwort gewartet ^^


    +puh+ Da bin ich beruhigt. Hab schon gedacht, dass ich mich als Typ mit mal gerade 1 Jahr so richtig auf Android hier als Klugscheißer gleich unbeliebt mache. +g+


    durch einen check mit instanceof fiel mir auf das trotz des types list das objekt trotzdem eine instanz von arraylist ist


    Ansonsten wird das mit dem Casten auch ziemlich schwierig. :)

    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!