代码之家  ›  专栏  ›  技术社区  ›  Phil

在不同线程上以安全、标准的方式在ListView中加载图像?

  •  1
  • Phil  · 技术社区  · 14 年前

    在提出这个问题之前,我已经搜索并阅读了这些问题: Lazy load of images in ListView Android - Issue with lazy loading images into a ListView

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

    • 每行包含一个 ImageView ,谁的 内容将从 互联网
    • 每一行的视图都像中一样循环使用 apidemo的列表14

    我最终想要的是:

    • 仅当用户 向他们滚动
    • 将不同线程上的图像加载到 保持响应能力

    我目前的方法:

    • 在适配器中 getView() 方法,除了 设置其他子视图时,我启动 从中加载位图的新线程 互联网。当加载线程 完成后,它返回 Bitmap 设置在 图片框 (我用 AsyncTask Handler )
    • 因为我回收 图片框 S,它可以 是我第一个想做的事 一个观点 Bitmap#1 ,以后需要 把它设置为 Bitmap#2 当用户 向下滚动。 位图1 可能发生 花的时间比 位图2 加载,所以 它可能最终会覆盖 位图2 在 风景。我通过维护 一 WeakHashMap 记得最后一个 位图 我想要 为该视图设置。

    下面是我当前方法的一个伪代码。我建议了其他细节,比如缓存,只是为了让事情更清楚。

    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) {}
    }
    

    我还有一个问题:如果在一些图像还在加载的时候活动被破坏了怎么办?

    • 装货时有风险吗 线程回调到ImageView 稍后,当活动已经 摧毁?
    • 此外, 异步任务 有一些全球性 线程池在下面,因此如果很长 当任务被取消时, 不再需要了,我可能会结束 浪费时间加载东西用户
      看不见。我目前的设计 在全球范围内保持这一点也是 丑陋,最终可能导致 超出我的范围的泄漏 理解。而不是 ImageLoader 像这样的单身汉, 我正在考虑真正创造 分离 保存图像类 对象为 不同的 Activities 然后,当一个 Activity 被摧毁,所有的 异步任务 将被取消。这是 太尴尬了?

    不管怎样,我想知道在安卓系统中是否有一种安全和标准的方法可以做到这一点。另外,我不知道iphone,但是有没有类似的问题,他们有没有一个标准的方法来完成这类任务?

    多谢。

    1 回复  |  直到 12 年前
        1
  •  4
  •   CommonsWare    14 年前

    我通过保持 怀卡什地图 我要为该视图设置位图。

    我采用了将所需图像的URL附加到 ImageView 通过 setTag() . 然后,当我下载图像时,我会再次检查 图片框 如果它与我刚下载的不同,我不会更新 图片框 因为它被回收了。我只是把它存起来。

    装货时有风险吗 线程回调到ImageView 稍后,当活动已经 摧毁?

    除了浪费CPU时间和带宽(以及电池)之外,我不知道还有什么风险。

    而不是让ImageLoader 像这样的单身汉,我在想 实际创建单独的ImageLoader 不同活动的对象,然后 当一个活动被破坏时, 它的异步任务将被取消。是 这太尴尬了?

    取消一个 AsyncTask 如果它已经在运行,那就不太容易了。我就让它完成。

    理想情况下,避免单身。要么使用 Service 或者通过你的 ImageLoader 到活动的下一个实例,通过 onRetainNonConfigurationInstance() (例如, isFinishing() false 在里面 onDestroy() ,这是一个旋转)。