多线程缓存imageView加载

时间:2011-05-17 15:55:04

标签: android multithreading caching imageview

我从人们制作的一些主要缓存/图像加载方法中获取了一些内容,并提出了一些使用浏览器缓存并加载图像的方法。

我的问题是有时图像不正确(与另一个图像重复),当我向下滚动图像列表时,图像会闪烁并更改为滚动。这是我必须忍受的东西吗?

是的我为每个imageView创建了一个新线程。我在队列中尝试了一个线程,加载时间太长。即使它们都是单独的线程,它也不应该使用错误的drawable来加载imageView。

这是我的代码。

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {

    if (imageView != null && urlString != null && urlString.length() > 0)
    {

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                if (imageView != null && urlString != null && !urlString.equals(""))
                {
                    URL url;
                    try {
                        InputStream is = fetch(urlString);

                        if (is != null) {
                            Drawable drawable = Drawable.createFromStream((InputStream)is, "src");
                            Message message = handler.obtainMessage(1, drawable);
                            handler.sendMessage(message);
                        } 
                    }
                    catch (MalformedURLException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    } catch (IOException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    }
                }

            }
        };
        thread.start();
    }

}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
    if (urlString != null)
    {
        URL url = new URL(urlString);

        URLConnection connection = url.openConnection();
        connection.setUseCaches(true);
        Object response = connection.getContent();
        if (response instanceof InputStream) {
            return (InputStream) response;
        }
    }
    return null;
}

* 修改 ***

这里的所有评论都是我的代码:我现在得到的是05-19 00:16:49.535:ERROR / AndroidRuntime(12213):java.lang.RuntimeException:Canvas:尝试使用回收的位图android。我尝试回收时,看看.ibitmap @ 4070dab0。

另外,当我没有回收时,一切都有效,除了我看到图像仍在闪烁。甚至有时会显示错误的图像。

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {

    if (imageView != null && urlString != null && urlString.length() > 0)
    {
        imageView.setTag(urlString);

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                Drawable d = imageView.getDrawable();
                if(d != null){
                     ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
                     imageView.setImageBitmap(null);
                }

                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                if (imageView != null && urlString != null && !urlString.equals(""))
                {
                    URL url;
                    try {
                        if (imageView.getTag().equals(urlString))
                        {
                            InputStream is = fetch(urlString);

                            if (is != null) {
                                Drawable drawable = Drawable.createFromStream((InputStream)is, "src");
                                Message message = handler.obtainMessage(1, drawable);
                                handler.sendMessage(message);
                            } 
                        }
                    }
                    catch (MalformedURLException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    } catch (IOException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    }
                }

            }
        };
        thread.start();
    }

}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
    if (urlString != null)
    {
        URL url = new URL(urlString);

        URLConnection connection = url.openConnection();
        connection.setUseCaches(true);
        Object response = connection.getContent();
        if (response instanceof InputStream) {
            return (InputStream) response;
        }
    }
    return null;
}

2 个答案:

答案 0 :(得分:3)

如果您使用ListView,则应该意识到ListView重新使用ImageView个实例。如果您的加载时间过长,并且绑定到的特定ImageView已被重用于另一个Image,那么您的实现将使用旧图像覆盖正确的图像。你需要:

  1. 修改您的fetchDrawableOnThread方法,将ImageView标记为要提取的网址:通常您会使用setTag并保存网址。
  2. 在提取InputStream后检入run方法,以确保getTag中的ImageView值与您刚刚提取的网址相匹配。如果它们不匹配,则ListView已重新使用此ImageView实例,您不应发送Message来显示图片。
  3. 在设置新的Drawable之前,你也应该在某个地方调用recycle来清理你正在消耗的Bitmap内存,但那是一个更长的故事。

    编辑:recycle故事实际上比我记忆中的简单(我查了一些旧代码)。基本上,在您为Drawable更新ImageView之前,请检查它是否已设置Drawable以及是否在其上调用recycle。所以像这样:

    ImageView iv = getImageView(); // fetch the image view somehow before you set it;
    Drawable d = iv.getDrawable();
    if(d != null){
         ((BitmapDrawable)iv.getDrawable()).getBitmap().recycle();
         iv.setImageBitmap(null);
    }
    

    这将回收前一个Drawable使用的内存,并应该有助于解决内存耗尽问题。

答案 1 :(得分:1)

基于Web视图的异步加载和缓存功能构建的快速简便的解决方案:

String htmlSnippet = "<html><head/><body>" +
                     "<img height=\"50px\" width=\"50px\" src=\"" + item.getImageUrl() + "\" />" +
                     "</body></html>";
imageWebview.loadData( htmlSnippet, "text/html", "utf-8" );