Android上的异步图像加载

时间:2012-02-01 22:21:42

标签: android multithreading image

Hello开发人员:)

我为Android制作了一个非常基本的图像获取器,可以在我的应用程序中从网上下载和显示位图,代码是:

public class BitmapFetcher {

    private static HashMap<String, SoftReference<Bitmap>> bitmapCache = new HashMap<String, SoftReference<Bitmap>>();

    public static Bitmap fetchBitmap(String urlString) {
        SoftReference<Bitmap> cachedBitmap = bitmapCache.get(urlString);

        if (cachedBitmap != null && cachedBitmap.get() != null) {
            return cachedBitmap.get();
        }

        try {
            InputStream is = fetch(urlString);

            Bitmap bitmap = BitmapFactory.decodeStream(is);
            SoftReference<Bitmap> softReferencedBitmap = new SoftReference<Bitmap>(bitmap);
            bitmapCache.put(urlString, softReferencedBitmap);

            return bitmap;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void fetchBitmapAsync(final String urlString, final ImageView view) {
        final Handler handler = new Handler() {
            public void handleMessage(Message message) {
                AsyncImageContainer imageContainer = (AsyncImageContainer) message.obj;

                imageContainer.applyImageToView();
            }
        };

        BitmapTaskRunnable asyncImageFetcherTask = new BitmapTaskRunnable(view, urlString, handler);

        new Thread(asyncImageFetcherTask).start();
    }

    public static InputStream fetch(String urlString) throws MalformedURLException, IOException {
        Log.d("BitmapFetcher", "fetch: " + urlString);
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);

        return response.getEntity().getContent();
    }

}

BitmapTaskRunnable.java:

public class BitmapTaskRunnable implements Runnable {

    private ImageView imageView;
    private String imageUrl;
    private Handler handler;

    public BitmapTaskRunnable() {
    }

    public BitmapTaskRunnable(ImageView imageView, String imageUrl, Handler handler) {
        setImageView(imageView);
        setImageUrl(imageUrl);
        setHandler(handler);
    }

    public void run() {
        Bitmap bitmap = BitmapFetcher.fetchBitmap(getImageUrl());

        AsyncImageContainer imageContainer = new AsyncImageContainer(getImageView(), bitmap);

        handler.sendMessage(handler.obtainMessage(0, imageContainer));
    }

    public ImageView getImageView() {
        return imageView;
    }

    public void setImageView(ImageView imageView) {
        this.imageView = imageView;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public Handler getHandler() {
        return handler;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

}

AsyncImageContainer.java:

public class AsyncImageContainer {

    private ImageView imageView;
    private Bitmap bitmap;

    public AsyncImageContainer() {
    }

    public AsyncImageContainer(ImageView imageView, Bitmap bitmap) {
        setImageView(imageView);
        setBitmap(bitmap);
    }

    public void applyImageToView() {
        getImageView().setImageBitmap(getBitmap());
    }

    public ImageView getImageView() {
        return imageView;
    }

    public void setImageView(ImageView imageView) {
        this.imageView = imageView;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
    }

}

正如我所说,这是非常基本的,具有非常基本的缓存,并且没有对创建的线程进行限制(这已经计划很快完成)。

好吧,当我需要在ListView中加载图像时,我正在遇到一种奇怪的行为,我做的是:我有一些扩展ArrayAdapter的类,并覆盖getView以显示我的布局,每当我需要一个图像时在它上面,我将做以下事情:

BitmapFetcher.fetchBitmapAsync(news.getChannelAvatar(), holder.channelAvatarView);

这应该开始使BitmapFetcher启动一个新线程,它将下载位图并向处理程序发送消息,将图像应用于视图(因为只有创建视图层次结构的线程才能修改它)。

列表中第2到第n个ImageViews的一切都很好,但是第一个结果总是发疯,换成已经下载的图像,直到所有图像都被加载并且它已经结束。然后,如果我拖动列表一点直到第一个结果消失并返回到顶部,它将显示正确的图像。

这真是让我烦恼,因为我做了一个更简单的代码版本(处理程序处理直接将Bitmap放在ImageView中,Runnable不存在,它是一个简单的匿名Thread对象{{ 1}}覆盖等等,并尝试这个版本认为某种方式run()正在失去对正确的ImageView或类似的东西的引用。

Android会在ListView中回收视图吗?如果没有,可能是什么原因?我在某个地方傻了吗?经过几天工作这段代码我已经失明了? :(

感谢所有帮助:D

1 个答案:

答案 0 :(得分:1)

对于我的所有延迟图像加载,我使用Prime。它甚至有一个如何在ListView中使用它的例子。