Odpowiedzi:
A teraz puenta: użyj pamięci podręcznej systemu.
URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
Bitmap bitmap = (Bitmap)response;
}
Zapewnia pamięć podręczną i pamięć podręczną flash-rom, współdzielone z przeglądarką.
grr. Żałuję, że ktoś mi to nie powiedział, zanim napisałem własnego menedżera pamięci podręcznej.
connection.getContent()
zawsze zwraca dla mnie InputStream, co robię źle?
Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
Odnośnie eleganckiego connection.setUseCaches
rozwiązania powyżej: niestety nie zadziała bez dodatkowego wysiłku. Będziesz musiał zainstalować ResponseCache
using ResponseCache.setDefault
. W przeciwnym razie HttpURLConnection
po cichu zignoruje setUseCaches(true)
bit.
Aby FileResponseCache.java
uzyskać szczegółowe informacje, zobacz komentarze u góry :
(Opublikowałbym to w komentarzu, ale najwyraźniej nie mam wystarczająco dużo karmy SOJ).
HttpResponseCache
, możesz znaleźć HttpResponseCache.getHitCount()
zwracające 0. Nie jestem pewien, ale myślę, że dzieje się tak, ponieważ żądany serwer WWW nie używa w tym przypadku nagłówków pamięci podręcznej. Aby mimo wszystko buforowanie działało, użyj connection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
.
.getContent()
metody, ponieważ odpowiedzi 304 nie mają powiązanej treści odpowiedzi według standardu RFC.
Przekonwertuj je na mapy bitowe, a następnie zapisz je w kolekcji (mapa haszowania, lista itp.) Lub możesz zapisać je na karcie SD.
Przechowując je w przestrzeni aplikacji przy użyciu pierwszego podejścia, możesz chcieć owinąć je wokół java.lang.ref.SoftReference, szczególnie jeśli ich liczba jest duża (aby były zbierane jako śmieci podczas kryzysu). Może to jednak spowodować przeładowanie.
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
zapisanie ich na karcie SD nie będzie wymagało ponownego załadowania; tylko zezwolenie użytkownika.
Uri
odniesienia do ścieżki, do którego można przejść, ImageView
i innych niestandardowych widoków. Ponieważ za każdym razem compress
będziesz tracić jakość. Oczywiście dotyczy to tylko algorytmów stratnych. Ta metoda umożliwiłaby nawet przechowywanie skrótu pliku i użycie go następnym razem, gdy zażądasz pliku z serwera za pośrednictwem If-None-Match
i ETag
nagłówków.
Służy LruCache
do wydajnego buforowania obrazów. Można przeczytać o LruCache
z Android Developer miejscu
Użyłem poniższego rozwiązania do pobierania i buforowania obrazów w systemie Android. Możesz wykonać poniższe czynności:
KROK 1:
nadaj klasie nazwę ImagesCache
. UżyłemSingleton object for this class
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
public class ImagesCache
{
private LruCache<String, Bitmap> imagesWarehouse;
private static ImagesCache cache;
public static ImagesCache getInstance()
{
if(cache == null)
{
cache = new ImagesCache();
}
return cache;
}
public void initializeCache()
{
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024);
final int cacheSize = maxMemory / 8;
System.out.println("cache size = "+cacheSize);
imagesWarehouse = new LruCache<String, Bitmap>(cacheSize)
{
protected int sizeOf(String key, Bitmap value)
{
// The cache size will be measured in kilobytes rather than number of items.
int bitmapByteCount = value.getRowBytes() * value.getHeight();
return bitmapByteCount / 1024;
}
};
}
public void addImageToWarehouse(String key, Bitmap value)
{
if(imagesWarehouse != null && imagesWarehouse.get(key) == null)
{
imagesWarehouse.put(key, value);
}
}
public Bitmap getImageFromWarehouse(String key)
{
if(key != null)
{
return imagesWarehouse.get(key);
}
else
{
return null;
}
}
public void removeImageFromWarehouse(String key)
{
imagesWarehouse.remove(key);
}
public void clearCache()
{
if(imagesWarehouse != null)
{
imagesWarehouse.evictAll();
}
}
}
KROK 2:
stwórz kolejną klasę o nazwie DownloadImageTask, która jest używana, jeśli bitmapa nie jest dostępna w pamięci podręcznej, pobierze ją stąd:
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{
private int inSampleSize = 0;
private String imageUrl;
private BaseAdapter adapter;
private ImagesCache cache;
private int desiredWidth, desiredHeight;
private Bitmap image = null;
private ImageView ivImageView;
public DownloadImageTask(BaseAdapter adapter, int desiredWidth, int desiredHeight)
{
this.adapter = adapter;
this.cache = ImagesCache.getInstance();
this.desiredWidth = desiredWidth;
this.desiredHeight = desiredHeight;
}
public DownloadImageTask(ImagesCache cache, ImageView ivImageView, int desireWidth, int desireHeight)
{
this.cache = cache;
this.ivImageView = ivImageView;
this.desiredHeight = desireHeight;
this.desiredWidth = desireWidth;
}
@Override
protected Bitmap doInBackground(String... params)
{
imageUrl = params[0];
return getImage(imageUrl);
}
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
if(result != null)
{
cache.addImageToWarehouse(imageUrl, result);
if(ivImageView != null)
{
ivImageView.setImageBitmap(result);
}
else if(adapter != null)
{
adapter.notifyDataSetChanged();
}
}
}
private Bitmap getImage(String imageUrl)
{
if(cache.getImageFromWarehouse(imageUrl) == null)
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = inSampleSize;
try
{
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream stream = connection.getInputStream();
image = BitmapFactory.decodeStream(stream, null, options);
int imageWidth = options.outWidth;
int imageHeight = options.outHeight;
if(imageWidth > desiredWidth || imageHeight > desiredHeight)
{
System.out.println("imageWidth:"+imageWidth+", imageHeight:"+imageHeight);
inSampleSize = inSampleSize + 2;
getImage(imageUrl);
}
else
{
options.inJustDecodeBounds = false;
connection = (HttpURLConnection)url.openConnection();
stream = connection.getInputStream();
image = BitmapFactory.decodeStream(stream, null, options);
return image;
}
}
catch(Exception e)
{
Log.e("getImage", e.toString());
}
}
return image;
}
KROK 3: Użycie z Twojego Activity
lubAdapter
Uwaga: jeśli chcesz załadować obraz z adresu URL z Activity
Class. Użyj drugiego Konstruktora z DownloadImageTask
, ale jeśli chcesz wyświetlić obraz z Adapter
użyj pierwszego Konstruktora z DownloadImageTask
(na przykład masz obraz w ListView
i ustawiasz obraz z 'Adaptera')
WYKORZYSTANIE Z AKTYWNOŚCI:
ImageView imv = (ImageView) findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();//Singleton instance handled in ImagesCache class.
cache.initializeCache();
String img = "your_image_url_here";
Bitmap bm = cache.getImageFromWarehouse(img);
if(bm != null)
{
imv.setImageBitmap(bm);
}
else
{
imv.setImageBitmap(null);
DownloadImageTask imgTask = new DownloadImageTask(cache, imv, 300, 300);//Since you are using it from `Activity` call second Constructor.
imgTask.execute(img);
}
UŻYCIE Z ADAPTERA:
ImageView imv = (ImageView) rowView.findViewById(R.id.imageView);
ImagesCache cache = ImagesCache.getInstance();
cache.initializeCache();
String img = "your_image_url_here";
Bitmap bm = cache.getImageFromWarehouse(img);
if(bm != null)
{
imv.setImageBitmap(bm);
}
else
{
imv.setImageBitmap(null);
DownloadImageTask imgTask = new DownloadImageTask(this, 300, 300);//Since you are using it from `Adapter` call first Constructor.
imgTask.execute(img);
}
Uwaga:
cache.initializeCache()
możesz użyć tego stwierdzenia w pierwszym działaniu swojej aplikacji. Po zainicjowaniu pamięci podręcznej nigdy nie będziesz musiał jej inicjować za każdym razem, jeśli używasz ImagesCache
instancji.
Nigdy nie jestem dobry w wyjaśnianiu rzeczy, ale mam nadzieję, że pomoże to początkującym, jak buforować użycie LruCache
i jego użycie :)
EDYTOWAĆ:
Obecnie istnieją bardzo znane biblioteki znane jako Picasso
i, Glide
które mogą być używane do bardzo wydajnego ładowania obrazów w aplikacji na Androida. Wypróbuj tę bardzo prostą i użyteczną bibliotekę Picasso na Androida i Glide na Androida . Nie musisz martwić się o obrazy w pamięci podręcznej.
Picasso umożliwia bezproblemowe ładowanie obrazu w Twojej aplikacji - często w jednej linii kodu!
Glide, podobnie jak Picasso, może ładować i wyświetlać obrazy z wielu źródeł, jednocześnie dbając o buforowanie i zachowując niewielki wpływ na pamięć podczas wykonywania manipulacji obrazami. Był używany przez oficjalne aplikacje Google (jak aplikacja na Google I / O 2015) i jest tak samo popularny jak Picasso. W tej serii przyjrzymy się różnicom i zaletom Glide nad Picassem.
Możesz również odwiedzić blog, aby zobaczyć różnicę między Glide i Picasso
if(cache == null)
co rozwiązało mój problem! :)
Aby pobrać obraz i zapisać na karcie pamięci, możesz to zrobić w ten sposób.
//First create a new URL object
URL url = new URL("http://www.google.co.uk/logos/holiday09_2.gif")
//Next create a file, the example below will save to the SDCARD using JPEG format
File file = new File("/sdcard/example.jpg");
//Next create a Bitmap object and download the image to bitmap
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
//Finally compress the bitmap, saving to the file previously created
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));
Nie zapomnij dodać pozwolenia internetowego do swojego manifestu:
<uses-permission android:name="android.permission.INTERNET" />
Rozważałbym użycie pamięci podręcznej obrazu droidfu. Implementuje zarówno pamięć podręczną obrazów w pamięci, jak i na dysku. Otrzymujesz również WebImageView, który korzysta z biblioteki ImageCache.
Oto pełny opis droidfu i WebImageView: http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
Wypróbowałem SoftReferences, są zbyt agresywnie odzyskiwane w Androidzie, że czułem, że nie ma sensu ich używać
SoftReference
plików. Zalecają używanie ich LruCache
zamiast tego.
Jak zasugerował Thunder Rabbit, ImageDownloader jest najlepszy do tego zadania. Znalazłem też niewielkie zróżnicowanie klasy na:
http://theandroidcoder.com/utilities/android-image-download-and-caching/
Główna różnica między nimi polega na tym, że ImageDownloader używa systemu pamięci podręcznej Androida, a zmodyfikowany wykorzystuje pamięć wewnętrzną i zewnętrzną jako buforowanie, przechowując obrazy w pamięci podręcznej przez czas nieokreślony lub do momentu, gdy użytkownik usunie je ręcznie. Autor wspomina również o kompatybilności z Androidem 2.1.
To dobry chwyt Joe. Powyższy przykład kodu ma dwa problemy - jeden - obiekt odpowiedzi nie jest instancją Bitmap (kiedy mój adres URL odwołuje się do pliku jpg, np. Http: \ website.com \ image.jpg, jest to plik a
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream).
Po drugie, jak wskazuje Joe, żadne buforowanie nie występuje bez skonfigurowania pamięci podręcznej odpowiedzi. Deweloperzy Androida są zmuszeni do rozwijania własnej pamięci podręcznej. Oto przykład, jak to zrobić, ale buforuje tylko w pamięci, co tak naprawdę nie jest pełnym rozwiązaniem.
http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/
Interfejs API buforowania URLConnection opisano tutaj:
http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
Nadal uważam, że jest to dobre rozwiązanie, aby przejść tę trasę - ale nadal musisz napisać pamięć podręczną. Brzmi fajnie, ale wolałbym pisać funkcje.
W oficjalnej sekcji szkoleniowej Androida jest na ten temat specjalny wpis: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Sekcja jest całkiem nowa, nie było jej tam, kiedy zadano pytanie.
Sugerowanym rozwiązaniem jest użycie LruCache. Ta klasa została wprowadzona w Honeycomb, ale jest również uwzględniona w bibliotece zgodności.
Możesz zainicjować LruCache, ustawiając maksymalną liczbę lub wpisy, a on automatycznie posortuje je według Ciebie i wyczyści rzadziej używane, gdy przekroczysz limit. Poza tym jest używana jako normalna mapa.
Przykładowy kod z oficjalnej strony:
private LruCache mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
Wcześniej SoftReferences były dobrą alternatywą, ale już nie, cytując z oficjalnej strony:
Uwaga: W przeszłości popularną implementacją pamięci podręcznej była pamięć podręczna mapy bitowej SoftReference lub WeakReference, jednak nie jest to zalecane. Począwszy od Androida 2.3 (poziom API 9), moduł odśmiecania pamięci jest bardziej agresywny w zbieraniu miękkich / słabych odniesień, co czyni je dość nieefektywnymi. Ponadto przed Androidem 3.0 (poziom interfejsu API 11) dane kopii zapasowej mapy bitowej były przechowywane w pamięci natywnej, która nie jest udostępniana w przewidywalny sposób, co może powodować krótkotrwałe przekroczenie limitów pamięci i awarię aplikacji.
Rozważ użycie biblioteki Universal Image Loader autorstwa Sergeya Tarasevicha . Pochodzi z:
Universal Image Loader umożliwia szczegółowe zarządzanie pamięcią podręczną pobranych obrazów z następującymi konfiguracjami pamięci podręcznej:
UsingFreqLimitedMemoryCache
: Najrzadziej używana mapa bitowa jest usuwana po przekroczeniu limitu rozmiaru pamięci podręcznej.LRULimitedMemoryCache
: Najmniej używana mapa bitowa jest usuwana po przekroczeniu limitu rozmiaru pamięci podręcznej.FIFOLimitedMemoryCache
: Reguła FIFO jest używana do usuwania, gdy zostanie przekroczony limit rozmiaru pamięci podręcznej.LargestLimitedMemoryCache
: Największa mapa bitowa jest usuwana po przekroczeniu limitu rozmiaru pamięci podręcznej.LimitedAgeMemoryCache
: Obiekt w pamięci podręcznej jest usuwany, gdy jego wiek przekroczy zdefiniowaną wartość .WeakMemoryCache
: Pamięć podręczna zawierająca tylko słabe odniesienia do map bitowych.Prosty przykład użycia:
ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://site.com/image.png";
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);
W tym przykładzie użyto domyślnej UsingFreqLimitedMemoryCache
.
To, co faktycznie zadziałało, to ustawienie ResponseCache w mojej klasie głównej:
try {
File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { }
i
connection.setUseCaches(true);
podczas pobierania mapy bitowej.
http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html
Google libs-for-android ma ładne biblioteki do zarządzania pamięcią podręczną obrazów i plików.
Zmagałem się z tym przez jakiś czas; odpowiedzi za pomocą SoftReferences zbyt szybko utraciłyby dane. Odpowiedzi, które sugerują utworzenie wystąpienia RequestCache, były zbyt skomplikowane, a ponadto nigdy nie mogłem znaleźć pełnego przykładu.
Ale ImageDownloader.java działa wspaniale dla mnie. Używa HashMap do momentu osiągnięcia pojemności lub do upływu limitu czasu oczyszczania, a następnie przenosi rzeczy do SoftReference, wykorzystując w ten sposób to, co najlepsze z obu światów.
Proponuję ZAPŁON to nawet lepiej niż Droid fu
https://github.com/kaeppler/ignition
https://github.com/kaeppler/ignition/wiki/Sample-applications
Jeszcze później odpowiedź, ale napisałem Menedżera obrazów Androida, który obsługuje buforowanie w sposób przezroczysty (pamięć i dysk). Kod znajduje się na Github https://github.com/felipecsl/Android-ImageManager
Późne odpowiedź, ale pomyślałem, powinienem dodać link do mojej strony, bo pisałem tutorial jak zrobić bufor obrazu dla android: http://squarewolf.nl/2010/11/android-image-cache/ Aktualizacja: strona została przeniesiona do trybu offline, ponieważ jej źródło było nieaktualne. Dołączam do @elenasys w jej radach dotyczących używania Ignition .
A więc do wszystkich ludzi, którzy natknęli się na to pytanie i nie znaleźli rozwiązania: mam nadzieję, że Ci się spodoba! = D
Późna odpowiedź, ale myślę, że ta biblioteka bardzo pomoże w buforowaniu obrazów: https://github.com/crypticminds/ColdStorage .
Po prostu dodaj adnotację ImageView za pomocą @LoadCache (R.id.id_of_my_image_view, "URL_to_downlaod_image_from), a zajmie się pobieraniem obrazu i ładowaniem go do widoku obrazu. Możesz również określić obraz zastępczy i załadować animację.
Szczegółowa dokumentacja adnotacji znajduje się tutaj: - https://github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation