Android 缓存位图-检查当前VM预算
我想知道是否有人在一个设计为缓存尽可能多的动态发现的图像的体系结构中有任何经验 在我的情况下,我为从列表视图中单击的每个项目请求一个位图。这些位图通过网络接收(就处理或网络I/O而言,这不是一种便宜的操作)。用户可能会从列表中加载一些项目,并在它们之间切换。我缓存这些图像,以便后续对项目的请求将导致即时加载位图:Android 缓存位图-检查当前VM预算,android,caching,architecture,bitmap,out-of-memory,Android,Caching,Architecture,Bitmap,Out Of Memory,我想知道是否有人在一个设计为缓存尽可能多的动态发现的图像的体系结构中有任何经验 在我的情况下,我为从列表视图中单击的每个项目请求一个位图。这些位图通过网络接收(就处理或网络I/O而言,这不是一种便宜的操作)。用户可能会从列表中加载一些项目,并在它们之间切换。我缓存这些图像,以便后续对项目的请求将导致即时加载位图: private Bitmap bitmap = null; public Bitmap getBitmap(int id) { if(bitmap == null) bitma
private Bitmap bitmap = null;
public Bitmap getBitmap(int id) {
if(bitmap == null) bitmap = expensiveNetworkOperationToGetBitmap(id);
return bitmap;
}
(请忽略线程阻塞问题)
然而,用户完全有可能浏览许多项目。每一个都会慢慢占用一片内存,最终超出预算并导致应用程序崩溃
这让我提出了一个问题:是否可以动态检测缓存何时达到限制?如果可以,则可以在添加新图像时开始销毁旧图像。请参见下面的示例代码:
private HashMap<Integer, Bitmap> bitmaps = new HashMap<Integer, Bitmap>();
public Bitmap getBitmap(int id) {
Bitmap bitmap;
if(!bitmaps.contains(id)) {
if(// Bitmap budget close to being exeeded?) {
bitmaps.keyValues().get(0).recycle();
bitmaps.remove(bitmaps.keyValues().get(0));
}
bitmap = expensiveNetworkOperationToGetBitmap(id);
bitmaps.put(id, bitmap);
else {
bitmap = bitmaps.get(id);
}
return bitmap;
}
private HashMap bitmap=new HashMap();
公共位图getBitmap(int id){
位图;
如果(!bitmaps.contains(id)){
如果(//位图预算接近被执行?){
bitmaps.keyValues().get(0.recycle();
移除(bitmaps.keyValues().get(0));
}
位图=expensiveNetworkOperationToGetBitmap(id);
位图.put(id,位图);
否则{
位图=位图。获取(id);
}
返回位图;
}
再一次,请忽略从HashMap弹出第一个位图效率低下的问题
我希望有人能对注释掉的部分提出建议。如果有比这更好的解决方案,那就太好了。您无法检测到,那么您就达到了堆的限制(在运行时AFAIK无法获得占用堆的大小) 这就是我要做的 我有一个listView,可以检测在任何时候显示了多少图像(可见行|列)。添加新位图时,我会在适配器中检查哪些缓存的图像不会被任何imageview显示。如果我找到一个或多个I.recycle()它们。当我需要重新加载图像时(已存储到手机的内存缓存中)我不仅检查缓存中是否存在该图像,还检查位图是否被回收。 如果回收了,我会重新编码,如果没有,我会再次下载。 这是最安全的操作。我还根据显示的位置缩小了图像的比例。 例如,如果我有一张200x200的图像,而imageView只有20x20,我会将图像缩小10倍 通过这种方式,android将处理我在上面显示的每一张图像。这一切都是关于位图的回收,就像android回收图像视图一样 您必须知道当前可见的位图数量,以便循环使用其余的位图 另外一个方便的技巧是,当您编码图像时(首先必须将其下载到缓存),使用文件描述符进行编码
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
o2.inPurgeable = true;
o2.inTempStorage = new byte[16 * 1024];
o2.inDither = true;
o2.inInputShareable = true;
FileInputStream fs = null;
try {
fs = new FileInputStream(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (fs != null) {
Bitmap b = BitmapFactory.decodeFileDescriptor(fs.getFD(),null, o2);
}
但是如果你做得很好,你就不需要fileDescriptor(谁更慢)如果你想让你的缓存基本上无限地增长到VM堆的极限,你可以使用类似于位图软引用的集合
您可能还希望在activity onLowMemory()中实现,以便更好地使用系统,因为您希望拥有如此大的缓存。我也在做类似的事情,但不是回收未显示的图像,而是保留最小的图像大小(50x70)px.如果你为所有未显示的内容回收图像,那么当列表滚动时,你肯定会遇到a)大量请求和b)丢失图像,直到你停止…?是的,使用我的方法,我不会在投掷时填充图像。(滚动可以且快速)。保留小图像的好方法,但同样,如果您不确切知道图像大小或某个点上有多少图像/行,最终您甚至必须回收最小的图像。您可以进行混合。在显示大图像时回收它们,并保留一个软参考,容量为小图像的两倍,然后再进行回收循环。无论如何,这是一个自定义作业,取决于您如何下载以及如何显示位图。如果无法检测正在使用的VM堆的大小,则此答案最接近正确。