Cache - Suche nach guter Lösung

  • Halli, hallo


    keine Frage nach Code, sondern mehr so nach einem Lösungansatz bzw. best Case bei minimalem Speicheraufwand.


    Folgendes Scenario


    von Datenquelle A bekomme ich einen Namen. Diese Namen stehen in direktem Zusammenhang mit einem Bild.
    Da ein gewisser Satz an Daten stets und ständig wiederkehrt habe ich mir 2 Arrays angelegt und dort Namen/Bildnamen hinterlegt. Die Bilder liegen
    als drawables vor.


    So nun kann es sein das neue Datensätze dazu kommen und für diesen Fall hatte ich bisher ein Standard-Dummy Bild was angezeigt wurde. (Also Name wurde angeziegt aber mit Standardbild)


    Nun habe ich mir eine Lösung gebaut, welche mir auch die "unbekannten" Bilder nachlädt und anzeigt.


    -> Standardabfrage prüft die Arrays -> wenn Name/Bild da dann wird die Resource geladen
    -> wenn Abfrage negativ verläuft -> Namen/Bildbeziehung sind in einer Datenquelle B (Webserver/MySQL) hinterlegt, hole ich mir per JSON
    den Bildnamen und lade das entsprechende Bild vom Webserver B nach
    -> funktioniert auch alles schön.


    Dem Experten fällt bereits der Hasenfuss bei der Nummer auf. Die Images werden in einem Cache gepuffert. Jedoch muss ich vorher immer noch die JSON-Abfrage durchgeführt werden. Das scheitert bei Offline und schon lande ich wieder beim meinem Fallback Standardbild.


    Lange Rede, wenig Sinn. Ich möchte also auch die JSON Abfrage bzw. Response cachen.
    Also im Grunde muss ich mir nur zwei Stringpaare ablegen. (Name/BildUrl)



    Was ist den jetzt der Königsweg? (gehen wir mal davon aus das die externen Namen/Bildbeziehungen niemals die 40 Einträge Marke übersteigen)


    Anmerkung: Bilder werden relativ häufig auf der Startseite genutzt, sollte also Geschwindigkeitsoptimiert sein


    -> einfach in die Sharedpreferences pumpen ?
    -> Arraylist/Klasse bei Applicationstart aus lokalen Cachedatei (xml/json/textfile) generieren, bei Applicationende würde die Arraylist gespeichert werden, falls neue Einträge dazugekommen sind
    -> eigene SQLite Database Lösung


    -> andere Ideen ???



    Wie gesagt die Anzahl der "Fremdeinträge" ist überschaubar, wichtig wäre die Offlineverfügbarkeit.

  • Hoi,


    ich benutze als Cache SQLite. Mein Primary Key ist die Bild-URL, der Value ist ein Blob. Bekomme ich jetzt via JSON/RSS 20 Items, durchlaufe ich diese und setze als Bild ein Standard-Bild. Nebenbei Erzeuge ich einen AsyncTask, den ich einem ThreadPoolExecutor hinzufüge, der je nach CPU-Kernen des Devices eben diese Zahl gleichzeitiger Elemente bearbeiten kann. Der AsyncTask bekommt meine ImageView mit, um im onPostExecute dann das Dummy-Bild durch das echte Bild zu ersetzen. Ist der AsyncTask dran, schaut er in die DB, ob ein Eintrag existiert. Wenn Ja, wurstet er den Blob in ein Drawable und gibt das der ImageView. Wenn nicht, lädt er es erst runter, wirft es in die DB und gibt es dann zurück.


    Damit das ganze nicht in einem MemoryLeak endet rechne ich die Bilder bevor sie als Blob in die DB kommen noch auf max-width=Device-Width, damit ich kein unnötig riesiges Bild habe. Bzw. Wenn ich nur ein Thumbnail brauche rechne ich max-width=Device-Width / 3, so passt noch etwas Text daneben ...



    Gruß,
    matze

  • Ich würde ebenfalls die Datenbank nutzen.
    Da ich allerdings MySQL verseucht bin und keine Ahnung habe, wie die Zugriffszeiten auf dem Device aussehen, würde ich von einem BLOB absehen und statt dessen die File URL ablegen. So hätte man natürlich einen weiteren Zugriff auf den Speicher, dafür aber eine schlankere und damit im Allgemeinen flinkere Datenbank.


    Herunterrechnen der Bilder ist natürlich Pflicht, sonst knallt es ziemlich schnell mal auf einigen Geräten.

    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,


    also eines meiner Projekte hat auf dem Gerät 28,5MB und beherbergt 103 Bilder, von denen je die große Ansicht und das Thumbnail als Blob in der DB gehalten werden. Die verbergen sich alle hinter einem Menüpunkt und werden alle untereinander mit meiner beschriebenen Methode angezeigt und auf einem Samsung Galaxy Nexus passiert das (mal gecached) so flott, dass man die Dummy-Bilder nicht sieht. Beim allerersten Mal ist es natürlich schon etwas fad langsam, wenn er erstmal 100 Bilder verwursten muss.


    Da Phil hier von 40 Einträgen spricht, müsste mein Aufbau eigentlich ausreichend sein.


    Gruß,
    matze

  • Danke für euren Input.


    Matze hat recht ich könnte natürlich gleich die Bilder mit in der DB speichern (40 ist hier wirklich das absolute Maximum was anfallen würde und die Bilder sind nur 84x84 Pixel groß - also Speicherplatztechnisch im Rahmen.)
    Ich schau mir das Blob speichern in SQLite heute Abend mal an.
    Ich hatte zwar den ImageCache und das Image runterladen schön per Volley gelöst, weil mir das den ganzen AsyncTaskWahnsinn abnimmt - aber das sollte schnell wieder anzupassen sein.

  • Hallo Matze,


    ein Wort vorn weg es funktioniert alles wie es soll. Ich denke gute Lösung mit der DB.


    Code
    Wenn du ein paar Codeschnipsel brauchst kann ich dir welche liefern, ich hab einiges an Nerven an dem Thema gelassen


    Die Codeschnipsel würden mich natürlich auch interessieren (nur zur Lernzwecken), aber noch eher so die Probleme auf die du gestossen bist - eventl sind das ja die selben die ich auch hatte.


    Mein Aufbau sieht jetzt wie folgt aus.


    -> Prüfe ob Bild in der lokalen Resourcenliste -> wenn ja anzeigen


    -> NEIN -> Prüfen ob Bild in der lokalen Datenbank vorhanden -> JA -> ByteArray zu Image wandeln -> Anzeigen


    -> NEIN -> wenn Online -> hole vom Webserver URL des Bildes -> Asynctask zum Laden des Bildes wird angestossen Speicerung in der DB -> Anzeigen des Bildes


    -> NEIN ich bin nicht Offline -> dann mache nix (Fallback Standard Image wird angezeigt)



    Den JSON runterladequatsch per Volley lasse ich mal weg und zeige nur mal meinen ASyncTask.


    Die einzige Schwierigkeit die ich hatte, ich muss dem Asynctask ein paar (4) Parameter mitgeben um all meine Wünsche zu handlen.


    Name des Bildes -> wird Unique Index für die DB
    URL des Bildes -> zum runterladen
    ImageView -> damit nach dem Speichern das Bild auch angezeigt wird
    ApplicationContext -> all das befindet sich in einem Fragment und im AsyncTask bekomme ich keinen ContextZugriff auf meine Application (da gibt es einen Absturz ) -> vermutlich weil ein getActivityaufruf ins Leere greift, wenn ich beim Scrollen des Viewpagers bin. -> Contextzugriff deswegen, weil ich den DB Zugriff ganz Oldskool per SQLiteHelperKlasse ect pp gelöst habe (ContentProvider fand ich überdimensioniert) und mein DatenbankObjekt hinterlege ich in meiner ApplicationInstanz.


    Mein Asynctask sieht dann so aus...




    wappendatasource = ist meine Datenbank Zugriffshilfsklasse

  • Hoi,


    also so grundlegend habe ich es auch, nur dass ich keine lokale Ressourcenliste habe, sondern immer in die DB greife.


    Ich geh mal meine Reihenfolge der Aufrufe durch:


    Ich baue eine ImageView und setze das Bild, wobei item.getImage() nur einen String liefert. Also die URL eigentlich.

    Java
    ImageView iv = new ImageView(this);
    			iv.setAdjustViewBounds(true);
    			iv.setPadding(5, 5, 5, 5);
    			// prüfe, ob mein item (bean) ein Image hat
    			if (item.getImage() != null && !item.getImage().isEmpty()) {
    				ImageLoader.getInstance().displayImage(this, item.getImage(), iv, true);
    			} else {
    				iv.setImageResource(R.drawable.default_video);
    			}


    Die displayImage sieht so aus


    Mein ImageTask ist nicht ganz konform, der hat irgendwie einen hau weg, ruft die onPostExecute nicht zuverlässig auf allen Devices auf, deshalb sieht er so aus


    Der handelt sowohl Online als auch Offline Bilder. Also welche, die im assets-Ordner liegen.


    Mein PhotoHandler hat die getImage, die in die DB greift und den blob holt. Wenn der leer ist holt er sich das File vom assets bzw. von der URL frisch. Ich geh mal nur auf getImageFromUrl ein


    das id.execute().get() hebelt zwar den AsyncTask etwas aus aber dem onPostExecute trau ich nicht mehr. Im ImageDownloader mach ich im Grunde das gleiche wie du, URLConnection aufbauen und Content abgreifen. Nur kleiner machen tu ich das Bild gleich, weil sonst später der RAM aus geht


    Das SplashScreen.deviceWidth und height lese ich ein mal im SplashScreen direkt am Anfang aus

    Java
    Display display = SplashScreen.this.getWindowManager()
    						.getDefaultDisplay();
    deviceWidth = display.getWidth();
    deviceHeight = display.getHeight();


    Ist deprecated, muss ich mal schaun, durch was ich das noch ersetze.
    Meine Tools.calculateInSampleSize sieht so aus


    und ist aus irgend einem Forum entwendet ^^


    Ganz Ganz wichtig ist dann noch das image.recycle() vor dem zweiten decodeStream, weil ältere Android-Versionen (glaub unterhalb Honeycomb) das alte image nicht selbst recyclen, wenn die Variable überschrieben wird und man hat das Bild ein mal in zu groß im RAM und dann ein zweites mal Kleiner, womit der Sinn der Aktion dahin währe.


    Glaub hab nix vergessen, also der Ansatz ist derselbe, wie du ihn jetzt auch verwirklicht hast. Nur hab ich extreme Probleme gehabt mit großen Bildern. Ich musste die aus Facebook auslesen und bekomm dort wenns blöd her geht ziemlich große Bilder. Facebook bietet jedes Bild zwar in glaub 9 verschiedenen Größen an, von dene auch das der deviceWidth entsprechend passendste genommen wird, nur kanns auch sein, dass das 1920x1080 oder so ist, da die Handys ja auch immer besser werden.


    Sollte ich noch einen Codeschnipsel vergessen haben oder etwas unklar ist, meld dich ;)


    Gruß,
    matze

Jetzt mitmachen!

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