1

在提出这个问题之前,我已经搜索并阅读了这些问题: 在 ListView Android 中延迟加载图像 - 将图像延迟加载到 ListView 中的问题

我的问题是我有一个ListView,其中:

  • 每行包含一个ImageView,其内容将从互联网加载
  • 每一行的视图都像 ApiDemo 的 List14 一样被回收

我最终想要的:

  • 延迟加载图像,仅当用户滚动到它们时
  • 在不同的线程上加载图像以保持响应能力

我目前的做法:

  • 在适配器的getView()方法中,除了设置其他子视图之外,我还启动了一个从 Internet 加载位图的新线程。当该加载线程完成时,它会返回Bitmap要设置的ImageView(我使用AsyncTaskor来执行此操作Handler)。
  • 因为我recycle ImageViews,所以可能是我先想用 设置一个视图Bitmap#1,然后想Bitmap#2在用户向下滚动的时候设置它。Bitmap#1可能碰巧需要比Bitmap#2加载更长的时间,因此它最终可能会覆盖Bitmap#2视图。我通过维护一个WeakHashMap记住Bitmap我想为该视图设置的最后一个来解决这个问题。

下面是我当前方法的一些伪代码。我省略了缓存等其他细节,只是为了保持清楚。

public class ImageLoader {

    // keeps track of the last Bitmap we want to set for this ImageView
    private static final WeakHashMap<ImageView, AsyncTask> assignments
                                    = new WeakHashMap<ImageView, AsyncTask>();

    /** Asynchronously sets an ImageView to some Bitmap loaded from the internet */
    public static void setImageAsync(final ImageView imageView, final String imageUrl) {
        // cancel whatever previous task
        AsyncTask oldTask = assignments.get(imageView);
        if (oldTask != null) {
            oldTask.cancel(true);
        }

        // prepare to launch a new task to load this new image
        AsyncTask<String, Integer, Bitmap> newTask = new AsyncTask<String, Integer, Bitmap>() {

            protected void onPreExecute() {
                // set ImageView to some "loading..." image
            }

            protected Bitmap doInBackground(String... urls) {
                return loadFromInternet(imageUrl);
            }

            protected void onPostExecute(Bitmap bitmap) {
                // set Bitmap if successfully loaded, or an "error" image
                if (bitmap != null) {
                    imageView.setImageBitmap(bitmap);
                } else {
                    imageView.setImageResource(R.drawable.error);
                }
            }
        };
        newTask.execute();

        // mark this as the latest Bitmap we want to set for this ImageView
        assignments.put(imageView, newTask);
    }

    /** returns (Bitmap on success | null on error) */
    private Bitmap loadFromInternet(String imageUrl) {}
}

我仍然遇到的问题:如果 Activity 在某些图像仍在加载时被破坏怎么办?

  • 当 Activity 已经销毁时,加载线程稍后回调 ImageView 是否有任何风险?
  • 此外,AsyncTask它下面有一些全局线程池,所以如果不再需要冗长的任务时没有取消它们,我最终可能会浪费时间加载用户
    看不到的东西。我目前在全球范围内保留这个东西的设计太丑陋了,最终可能会导致一些我无法理解的泄漏。ImageLoader我没有像这样制作 单例,而是考虑实际ImageLoader为 different 创建单独的对象Activities,然后当 an Activity被销毁时,它的所有 内容都AsyncTask将被取消。这也太尴尬了吧?

无论如何,我想知道在 Android 中是否有一种安全且标准的方法来执行此操作。另外,我不了解 iPhone,但那里有类似的问题吗?他们有标准的方法来完成这种任务吗?

非常感谢。

4

1 回答 1

4

我通过维护一个 WeakHashMap 来解决这个问题,该 WeakHashMap 记住我想为该视图设置的最后一个位图。

我采用了将所需图像的 URL 附加到ImageViewvia的方法setTag()。然后,当我下载图像时,我会仔细检查ImageViewURL——如果它与我刚刚下载的不同,我不会更新ImageView,因为它被回收了。我只是缓存它。

当 Activity 已经销毁时,加载线程稍后回调 ImageView 是否有任何风险?

除了浪费一点 CPU 时间和带宽(因此还有电池)外,我不知道有任何风险。

我没有像这样使 ImageLoader 成为单例,而是考虑为不同的 Activity 创建单独的 ImageLoader 对象,然后当 Activity 被销毁时,它的所有 AsyncTask 都将被取消。这也太尴尬了吧?

AsyncTask如果它已经在运行,取消它并不是很容易。我只是让它运行完成。

理想情况下,避免单例。要么使用 a Service,要么通过(例如,is in ,所以这是轮换)将您传递ImageLoader给您的活动的下一个实例。onRetainNonConfigurationInstance()isFinishing()falseonDestroy()

于 2010-05-22T16:48:03.613 回答