Drawable和单个可重用的位图,哪个更好地处理内存?

20

据我所知(并不一定正确),当应用程序完成对Drawables的使用时,它们通常会被正确地从内存中删除。然而,Bitmaps需要手动回收,并且有时甚至需要编写特殊的类来正确处理它们。我的问题是,在内存和泄漏方面,是否更有益于仅使用Drawables,就像这样:

myView.setBackgroundDrawable(getResources().getDrawable(R.drawable.my_image));
myView1.setBackgroundDrawable(getResources().getDrawable(R.drawable.my_image1));
myView2.setBackgroundDrawable(getResources().getDrawable(R.drawable.my_image2));

不是使用位图这样的东西:

Bitmap tmpBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image);
myView.setImageBitmap(tmpBitmap);

tmpBitmap.recycle();
tmpBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image1);
myView1.setImageBitmap(tmpBitmap);

tmpBitmap.recycle();
tmpBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image2);
myView2.setImageBitmap(tmpBitmap);
tmpBitmap.recycle();

当然,我也读到过必须小心Bitmap的recycle()方法,因为它们可能在仍在使用时被移除?似乎这些问题以不同形式不断出现,但我无法从任何人那里得到直接的答案。有人建议在每次使用后重用Bitmap并回收,而其他人建议使用Drawables和unbindDrawables()方法(这是我一直在使用的):

private void unbindDrawables(View view) {
    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}

非常感谢能提供任何有用的见解。

2个回答

9
位图不需要手动回收。它们会像Drawable和其他对象一样被垃圾回收。同样的,除非在特殊情况下,你不需要解绑定Drawables。看起来你读了很多误导性的信息。
在某些情况下,回收位图和解绑定Drawables可能是有用的(例如,如果你的应用程序操作大量位图数据或者以静态方式存储Drawable)。
你问题开头的两个示例是等价的。如果直接加载一个Drawable,Bitmaps会被代表你加载。如果手动加载位图并将其设置到ImageView中,它们就会被封装在Drawable中代表你加载。
首选第一种解决方案,因为它更简单,直到你真正需要它们才考虑解绑定和其他内存管理技术。

我询问的原因是因为我需要内存管理技术。我在按钮点击时操纵多个视图的图像,这是我的菜单系统。这很好,除非我滚动每个菜单,然后退出,并再次执行相同操作。几次后,应用程序会崩溃,因为“位图超过VM限制OOM”。不确定为什么会发生这种情况,但当我使用unbindDrawables()方法时,它就不会发生。或者至少我还没有能够重新创建它。 - While-E
3
在Honeycomb版本之前,处理位图时必须非常小心。在Honeycomb版本之前,位图是分配在Java垃圾回收器无法清理的内存空间中,这会导致内存泄漏。如果您使用Honeycomb版本或更高版本,则无需担心此问题。 - Joe Plante
1
位图分配在本地堆上并不会导致内存泄漏。这只意味着您必须等待终结器运行,然后位图才能被释放。 - Romain Guy

8

我支持Romain的建议,但我不确定您的问题是否与实际问题有关。我不知道您如何处理对Views的引用。也许您的应用程序中存在内存泄漏?Android中许多内存泄漏与Context有关。当一个Drawable附加到一个View上时,View会被设置为Drawable的回调。

TextView myView = new TextView(this);
myView.setBackgroundDrawable(getDrawable(R.drawable.my_bitmap));

在上面的代码片段中,这意味着 Drawable 具有对 TextView 的引用,后者本身具有对 Activity (即 Context )的引用,而该 Context 根据您的代码而定,可能引用了各种内容。
没有查看更多代码,我猜测当一个 Activity 被销毁时,通过将存储的drawable的回调设置为 null ,你走在了正确的轨道上。

谢谢,非常好的信息。我通过findViewById(R.Id.Image_name)获取我的视图;顺便说一句,不确定这是否会留下对上下文的引用。 - While-E

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接