我有一个覆盖Application
对象的对象。在其中,我有一个成员变量LongSparseArray
,其中键是long
类型的标识符,值是具有2个成员变量的对象:Bitmap
和{{{ 1}}用作时间戳。
这是我的全局图像缓存。偶尔会运行一个函数来查看时间戳并查看超过一小时的事物。
“年龄”是指它从long
删除整个条目。
这是我的问题:
假设我有LongSparseArray
Activity
。 ListView
中的每一行都有一个ListView
,其中填充了缓存中的图像。
ImageView
现在,假设用户点击了某个按钮,将其转到新的Bitmap image = ((MyApp)getApplicationContext()).getImage(id);
holder.imgImage.setImageBitmap(image);
。在新的Activity
上,之前已分配给前一Activity
年ListView
行中某一行的图片。
因此,回顾一下,全球Activity
中现在不再存在Bitmap
键/值条目。
Java LongSparseArray
真的能够被回收吗?在Bitmap
ImageView
的{{1}}中,ListView
是否仍然引用它?当然,假设Android没有回收Activity
使用的内存。
我问这个问题的原因是我以前的老化功能也会调用Activity
上的.Recycle()
。在这种情况下,当用户点击后退按钮并返回到使用该Bitmap
的上一个Activity
时,应用程序将崩溃,大概是因为Bitmap
不仅丢失了缓存,也来自内存。所以我刚刚删除了Bitmap
来电。
顺便说一下,一旦.Recycle()
从缓存中删除,并且具有该ID的对象再次显示在屏幕上,应用程序将再次下载Bitmap
并将其放入缓存中。如果前一个留在内存中,你可以看到这会产生问题。
另外,有没有人对更有效的解决方案有任何想法?
如果我设置Bitmap
会怎样?
有2个myImageView.setDrawingCacheEnabled(false);
使用此图片缓存。一个是在用户执行搜索后显示项目列表(及其图像)的搜索屏幕。另一个是用户随后选择保留的项目列表。
答案 0 :(得分:4)
问题:在位图上调用recycle()方法后,永远不应再次使用位图。如果尝试绘制位图,则会抛出异常。来自docs:
只有在确定位图为no时才应使用recycle() 更长时间的使用。如果你调用recycle()然后尝试绘制 位图,您将收到错误:" Canvas:尝试使用回收 位图"
在这种特定情况下,您已经回收了位图,但ListView
项ImageView
具有对位图的强引用。当您返回Activity时,ListView项目会尝试绘制位图,因此抛出异常。
位图内存管理:在Android 2.3.3
之前,位图的支持像素数据存储在本机内存中,位图本身存储在Dalvik内存中。因此,要释放本机内存,必须调用recycle
方法。
这是Bitmap.recycle
函数定义:
public void recycle() {
if (!mRecycled) {
if (nativeRecycle(mNativeBitmap)) {
// return value indicates whether native pixel object was actually recycled.
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
// Bitmap itself is collected.
mBuffer = null;
mNinePatchChunk = null;
}
mRecycled = true;
}
}
发布Android 3.0
,支持像素数据也存储在Dalvik内存中。当不再需要位图时,我们需要确保我们不要对位图进行任何强引用,以便对其进行垃圾回收。
解决方案:如果您仍然支持Android 2.3.3
及更低版本,则仍需要使用recycle
来释放位图。
您可以使用引用计数来跟踪ListView项目当前是否正在引用位图,这样即使它已经老化,也不会在位图上调用recycle
。
ListView适配器的getView
方法是将位图分配给ImageView的位置。在这里增加引用计数。您可以将setRecyclerListener附加到ListView,以便知道列表视图项何时放入回收站。这是您将减少位图的引用计数的位置。老化函数只有在引用计数为零时才需要回收位图。
您还可以考虑使用LruCache进行缓存,如docs中所述。
setDrawingCacheEnabled :通过使用true param调用此方法,下一次调用getDrawingCache
会将视图绘制到位图。视图的位图版本可以呈现在屏幕上。由于它只是一个位图,我们无法像实际视图那样与它进行交互。几个用例是:
ListView
时,将捕获并呈现所显示项目视图的位图。因此,滚动的视图不会经过测量和布局传递。答案 1 :(得分:0)
这个Bitmap真的能够被Java回收吗?还没有 由前一个ListView中的ImageView引用 活动?当然,假设Android还没有收回 该活动使用的内存。
Bitmap
使用了ListView
(一个强大的参考资料),因此dalvik无法回收其记忆。
显然你无法在位图上调用recycle
,否则会发生不好的事情(应用程序崩溃,例如)。
如果我设置myImageView.setDrawingCacheEnabled(false)会怎么样??
如果您禁用了绘图缓存,则每次需要重新绘制视图时,都会调用onDraw
方法。我对ImageView
不是很熟悉,您可以去阅读它深刻理解的来源。
(注意:当启用/禁用硬件加速时,绘图缓存的使用会有所不同,这里我假设您正在使用软件渲染)。
对于解决方案,您可以尝试以下方法:
ListView.getView
中,您可以检查当前使用的位图是否老化。它应该很简单,因为您知道第一次调用setImageBitmap
时的时间戳和最新的时间戳。如果它们不相同,则使用新的位图再次调用setImageBitmap
,旧的位图将被回收。希望这有帮助。
答案 2 :(得分:0)
关于,"此外,是否有人对更有效的解决方案有任何想法?"
毕加索图书馆将有助于解决您面临的问题http://square.github.io/picasso/
Picasso是#34;一款功能强大的Android图像下载和缓存库"
" Android上图像加载的许多常见陷阱由毕加索自动处理: