Answers:
现在的重点是:使用系统缓存。
URL url = new URL(strUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(true);
Object response = connection.getContent();
if (response instanceof Bitmap) {
Bitmap bitmap = (Bitmap)response;
}
提供与浏览器共享的内存和Flash-ROM缓存。
grr。我希望有人告诉我,在我编写自己的缓存管理器之前。
connection.getContent()
总是为我返回InputStream,我在做什么错?
Bitmap response = BitmapFactory.decodeStream((InputStream)connection.getContent());
关于上述优雅的connection.setUseCaches
解决方案:可悲的是,如果不付出额外的努力,它将无法正常工作。您将需要安装ResponseCache
using ResponseCache.setDefault
。否则,HttpURLConnection
将默默地忽略该setUseCaches(true)
位。
有关FileResponseCache.java
详细信息,请参见顶部的评论:
(我将其发布在评论中,但显然我没有足够的业障。)
HttpResponseCache
,您可能会发现HttpResponseCache.getHitCount()
返回的0。我不确定,但是我认为这是因为您请求的Web服务器在这种情况下不使用缓存头。要使缓存正常工作,请使用connection.addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
。
.getContent()
因为304响应没有RFC标准的关联响应主体。
将它们转换为位图,然后将其存储在Collection(HashMap,List等)中,或者可以将它们写入SD卡。
使用第一种方法将它们存储在应用程序空间中时,您可能希望将它们包装在java.lang.ref.SoftReference周围,特别是如果它们的数量很大(以便在危机期间将其垃圾回收)。但这可能会导致重新加载。
HashMap<String,SoftReference<Bitmap>> imageCache =
new HashMap<String,SoftReference<Bitmap>>();
将它们写入SD卡将不需要重新加载;只是用户权限。
Uri
可以传递给的路径引用ImageView
以及其他自定义视图。因为每次compress
,您都会失去质量。当然,这仅对有损算法正确。此方法还允许您甚至存储文件的哈希,并在下次通过If-None-Match
和ETag
标头从服务器请求文件时使用它。
用于LruCache
有效地缓存图像。您可以LruCache
从Android开发者网站了解
我已经使用以下解决方案在Android中下载和缓存图片。您可以按照以下步骤操作:
步骤1:
将Class命名为ImagesCache
。我用过Singleton 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();
}
}
}
第2步:
制作另一个名为DownloadImageTask的类,如果位图在缓存中不可用,将使用该类从此处下载:
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;
}
步骤3:从您Activity
或Adapter
注意:如果要从Activity
Class的URL加载图像。使用的第二个构造函数DownloadImageTask
,但是如果要Adapter
使用的第一个构造函数显示图像DownloadImageTask
(例如,您有一个图像,ListView
并且正在从“适配器”设置图像)
活动使用:
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);
}
适配器的用法:
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);
}
注意:
cache.initializeCache()
您可以在应用程序的第一个活动中使用此语句。初始化缓存后,如果您使用ImagesCache
实例,则无需每次都对其进行初始化。
我从不擅长解释事物,但希望这对初学者如何使用缓存LruCache
及其用法有所帮助:)
编辑:
现在,天有被称为非常著名的图书馆Picasso
,并Glide
可以在Android应用程序非常有效地用于加载图像。试试这个非常简单实用的库Picasso(适用于Android)和Glide(适用于Android)。您无需担心缓存图像。
Picasso允许在您的应用程序中轻松加载图像-通常只需一行代码!
像Picasso一样,Glide可以加载和显示来自许多来源的图像,同时在进行图像处理时还可以注意缓存并保持较低的内存影响。它已被正式的Google应用程序使用(例如Google I / O 2015的应用程序),并且与Picasso一样受欢迎。在本系列中,我们将探索Glide与毕加索的区别和优势。
您也可以访问博客,了解Glide和Picasso之间的区别
if(cache == null)
解决我的问题的人投票!:)
要下载图像并将其保存到存储卡,您可以这样操作。
//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));
不要忘记将Internet权限添加到清单中:
<uses-permission android:name="android.permission.INTERNET" />
我会考虑使用droidfu的图片缓存。它同时实现了内存中和基于磁盘的图像缓存。您还将获得一个利用ImageCache库的WebImageView。
以下是droidfu和WebImageView的完整说明:http ://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/
正如Thunder Rabbit所建议的那样,ImageDownloader是最适合的工作。我还在以下位置发现了该类的轻微变化:
http://theandroidcoder.com/utilities/android-image-download-and-caching/
两者之间的主要区别在于ImageDownloader使用Android缓存系统,而修改后的版本使用内部和外部存储作为缓存,可以无限期保留缓存的图像,或者直到用户手动将其删除为止。作者还提到了Android 2.1兼容性。
这是乔的一个好收获。上面的代码示例有两个问题-一个-响应对象不是Bitmap的实例(当我的URL引用jpg(例如http:\ website.com \ image.jpg)时,
org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl $ LimitedInputStream)。
其次,正如Joe指出的,没有配置响应缓存就不会发生缓存。Android开发人员只能滚动自己的缓存。这是一个这样做的示例,但是它仅缓存在内存中,这实际上并不是完整的解决方案。
http://codebycoffee.com/2010/06/29/using-responsecache-in-an-android-app/
URLConnection缓存API的描述如下:
http://download.oracle.com/javase/6/docs/technotes/guides/net/http-cache.html
我仍然认为这是可行的解决方案-但您仍然必须编写一个缓存。听起来很有趣,但是我宁愿编写功能。
Android的官方培训部分上有一个关于此的特殊条目:http : //developer.android.com/training/displaying-bitmaps/cache-bitmap.html
该部分是相当新的部分,在提出问题时不存在。
建议的解决方案是使用LruCache。该类是在Honeycomb上引入的,但它也包含在兼容性库中。
您可以通过设置最大数量或条目来初始化LruCache,当您超出限制时,它将自动对它们进行排序并清除使用较少的条目。除此之外,它还用作法线贴图。
来自官方页面的示例代码:
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);
}
以前,SoftReferences是一个不错的选择,但现在不再引用官方页面:
注意:过去,流行的内存缓存实现是SoftReference或WeakReference位图缓存,但是不建议这样做。从Android 2.3(API级别9)开始,垃圾收集器在收集软/弱引用方面更具侵略性,这使它们相当无效。另外,在Android 3.0(API级别11)之前,位图的备份数据存储在本机内存中,而本机内存无法以可预测的方式发布,这可能导致应用程序短暂超过其内存限制并崩溃。
考虑使用通用图像装载机库由谢尔盖Tarasevich。它带有:
Universal Image Loader允许使用以下缓存配置对下载的图像进行详细的缓存管理:
UsingFreqLimitedMemoryCache
:超过缓存大小限制时,将删除最不常用的位图。LRULimitedMemoryCache
:当超过缓存大小限制时,将删除最近最少使用的位图。FIFOLimitedMemoryCache
:超过缓存大小限制时,FIFO规则用于删除。LargestLimitedMemoryCache
:超过缓存大小限制时,将删除最大的位图。LimitedAgeMemoryCache
:当缓存对象的寿命超过定义值时,删除该对象。WeakMemoryCache
:仅具有弱引用位图的内存缓存。一个简单的用法示例:
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);
本示例使用default UsingFreqLimitedMemoryCache
。
实际上对我有用的是在Main类上设置ResponseCache:
try {
File httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
} catch (IOException e) { }
和
connection.setUseCaches(true);
下载位图时。
http://practicaldroid.blogspot.com/2013/01/utilizing-http-response-cache.html
Google的Android库libs有一个不错的库,用于管理图像和文件缓存。
我已经为此努力了一段时间。使用SoftReferences的答案将很快丢失其数据。建议实例化RequestCache的答案太乱了,而且我再也找不到完整的示例。
但是ImageDownloader.java对我来说效果很好。它使用HashMap,直到达到容量或清除超时为止,然后将事物移至SoftReference,从而充分利用了两者的优势。
甚至有更高的答案,但是我写了一个Android Image Manager,它透明地处理缓存(内存和磁盘)。该代码在Github上https://github.com/felipecsl/Android-ImageManager
迟到的回答,但我想我应该添加一个链接到我的网站,因为我已经写了一个教程如何使图像缓存为Android:http://squarewolf.nl/2010/11/android-image-cache/ 更新:在由于源已过时,该页面已脱机。我和@elenasys一起建议她使用点火。
因此,对于所有偶然发现此问题但仍未找到解决方案的人们:希望您喜欢!= D
答案较晚,但我认为该库在缓存图像方面会大有帮助:https : //github.com/crypticminds/ColdStorage。
只需使用@LoadCache(R.id.id_of_my_image_view,“ URL_to_downlaod_image_from)注释ImageView,它将负责下载图像并将其加载到图像视图中。您还可以指定占位符图像并加载动画。
注释的详细文档位于此处:-https : //github.com/crypticminds/ColdStorage/wiki/@LoadImage-annotation