【问题标题】:Using Google's ImageWorker to load images in ListView results in java.lang.RuntimeException使用 Google 的 ImageWorker 在 ListView 中加载图像会导致 java.lang.RuntimeException
【发布时间】:2014-10-12 23:23:38
【问题描述】:

最近,我开始使用 Google ImageWorker 类在后台线程上加载位图。此类处理所有事情,包括将位图放入 ImageView。谷歌在这里谈论这个类(它是图像操作和缓存的辅助类):http://developer.android.com/training/displaying-bitmaps/process-bitmap.html

在我的例子中,ImageView(s) 是 ListView 中列表项的一部分。这是我的 ArrayAdapter 上 getView 的有趣部分:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    View view = convertView == null ? inflater.inflate(R.layout.nodelist_item, null) : convertView;
    NodeHolder holder = getHolder(view);

    Node node = mList.get(position);
    holder.txtTitle.setText(node.Title);
    //Setting the rest of the different fields ...

    //And finally loading the image
    if(node.hasArt())
        worker.loadImage(node.ImageID, holder.imgIcon);
}

完整的getView方法可以看这里:http://pastebin.com/CJbtVfij

worker 对象是 ImageWorker 的一个非常简单的实现:

public class NodeArtWorker extends ImageWorker {

    protected int width;
    protected int height;

    public NodeArtWorker(Context context) {
        super(context);
        addImageCache(context);
        //Code for getting the approximate width and height of the ImageView, in order to scale to save memory.
    }

    @Override
    protected Bitmap processBitmap(Object data) {
        Bitmap b = getBitmap(data);
        if(b == null) return null;
        return Utils.resizeBitmap(b, width, height);
    }

    protected Bitmap getBitmap(Object data) {
        //Downloads the bitmap from a server, and returns it.
    }
}

这很好用,现在的性能比以前好多了。但是,如果我更改列表中某些项目的 ImageID,并调用 adapter.notifyDataSetChanged() 来重建视图(从而开始加载新位图),我会收到 RuntimeException:

0   java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@41adf448
1   at android.graphics.Canvas.throwIfRecycled(Canvas.java:1058)
2   at android.graphics.Canvas.drawBitmap(Canvas.java:1159)
3   at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:440)
4   at android.widget.ImageView.onDraw(ImageView.java:1025)
5   at android.view.View.draw(View.java:14126)

完整的堆栈跟踪可以在以下 pastebin 条目中看到,但可能并不有趣,因为它是典型的 Android 视图层次结构重绘堆栈跟踪:http://pastebin.com/DsWcidqw

我知道位图正在被回收,但我不明白究竟在哪里以及可以做些什么。由于我的代码直接来自 Google,而且据我所知,这是推荐的执行方式,我很困惑。

【问题讨论】:

  • 你能描述一下NodeNodeHolder的结构吗
  • 它位于第一个代码块正下方的 pastebin 中。 :)

标签: android caching memory-management android-listview bitmap


【解决方案1】:

我认为这不是 ImageWorker class 错误,这是

com.mobeta.android.dslv.DragSortListView.dispatchDraw(DragSortListView.java:797)

问题。因此,为了查看我的答案是否正确,只需删除该库并查看它是否有效。之后我认为最好的解决方案是:

1- 不要使用那个库。

2- 在 github 上报告问题。

3-自行解决并调试。

4- 或者您可以为图像持有人位图设置 null

holder.imgIcon.setBitmapDrawble(null);

然后拨打worker.loadImage

【讨论】:

  • 这也可能是正确的,但我已更改为使用 Picasso 图像加载库而不是 Google 的,这似乎有效(即使我仍在使用 DragSortListView)。
  • 毕加索可能会在不同的时间处理位图回收,因此DragSortListView 可以毫无问题地使用它。
【解决方案2】:

在 Android 上,Bitmaps 可以回收以供以后重新使用(比重新创建位图快得多)。
Bitmap#recycle()方法会将位图标记为已回收。
所以如果你尝试将这样一个回收的位图设置为 ImageView 或者在 Canvas 上绘制,你最终会得到这个异常:

java.lang.RuntimeException: Canvas: 试图使用回收的位图

您在问题上链接的官方演示正在处理回收的Bitmaps。
它使用专用方法hasValidBitmap() 并检查Bitmap#isRecycled() 值:

private synchronized boolean hasValidBitmap() {
    Bitmap bitmap = getBitmap();
    return bitmap != null && !bitmap.isRecycled();
}

所以你也需要这样做。当您在缓存中搜索位图时,在将其应用到 ImageView 之前,请检查它是否被回收。如果不是可以直接设置,否则需要更新Bitmap。

要从回收的位图创建新的位图,您可以使用Bitmap::createBitmap(Bitmap) 方法。

您可以在this page找到更多详细信息。

【讨论】:

  • 你可能是对的。我尝试了 Picasso 图像加载库,它似乎没有任何问题。
猜你喜欢
  • 2012-03-09
  • 1970-01-01
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-09
  • 2014-05-06
相关资源
最近更新 更多