14

我有一个关于我是否应该(以及如何)存储从网络加载的图像的问题。假设我正在从我的 Android 应用程序调用 Web 服务。在此 Web 服务中,我获得了 Web 上图像的 URL。我下载并在 ListView 中列表项的左侧显示此图像。我的问题是,我应该使用什么方法来存储图像?我是不是该:

  1. 将其保存到 SDCard,在创建 ListView 时检查它是否存在(在后续请求中)并在必要时重新下载(偶尔更新图像,以防它发生变化)。
  2. 使用 Context.getCacheDir() 将其存储在缓存中,但可能会被迫更频繁地重新下载它,因为我不能依赖保留在缓存中的图像。
  3. 始终下载它并且永远不要存储图像。

图像文件本身相当小,但我希望一些用户可以下载/存储数十个这样的小图像。哪种方法效果最好,和/或首选方法是什么?

作为一个附带问题,我应该首先在我的 ListView 中加载所有图像(并可能锁定 UI 一段时间)还是异步加载它们但同时显示一个占位符图形(这可能有点“丑陋”)?这里的标准是什么?

4

4 回答 4

21

关于存储位置: 答案取决于下载的内容和数量。然后你做出选择。

例如:如果您正在下载临时的、数量较少(较少远程获取)和大小(较少内存)并且是 Activity 本地的东西,那么您应该考虑使用 SoftReferences 将图像保存在内存中。SoftReferences 可能会导致重新获取,但由于项目数量很少,它应该是可以承受的。

但是,如果要下载的项目数量超过某个阈值(意味着更多的获取和内存),您应该考虑通过缓存它们来减少获取以及运行时内存消耗。在这里,您可以选择将它们保存在 SD 卡或临时存储(应用程序本地缓存目录)上。对于较小且仅在应用程序上下文中有意义的项目(例如缩略图),用户通常不会在您的应用程序之外使用它。因此,您可以将此类内容存储在缓存目录中。使用它们最好的部分是您不必清理烂摊子。它是自动处理的。不过,它可能会导致重新获取。

但是,如果下载的项目很大并且可以独立于应用程序的上下文之外,例如图片、视频、音频剪辑,那么 SD 卡应该是您的选择。您还应该阅读: Handling large Bitmaps to avoid OOM error during BitmapFactory.decodeStream(..)

请注意,您还可以在此处查看使用数据库是否有帮助。看到这个

在 ListView 中延迟加载项目时的一些注意事项: 您应该在后台进行加载,而不是阻塞 UI 线程。您应该考虑在下载项目时显示临时图像。这在许多本机应用程序中很明显。有关延迟加载的示例实现,请参阅此。此外,对于大型列表,您可以实现 SlowAdapter 模式(查看 API 演示)。它基本上在列表滚动时停止下载。

可以在这里为您提供帮助的示例项目:

Romain Guy 的 Shelves 项目使用两级缓存,其中他使用内存缓存(包含 SoftReferences 的 HashMap)和存储在 Sdcard 上。在此处浏览源代码

还有一些 Mark Murphy 编写的开源库(CWAC)和 DroidFu 可以在这里提供帮助。

祝你好运!

于 2010-01-13T08:58:37.170 回答
3

关于您的“附带问题”-我认为异步加载它们将是首选行为,特别是因为您需要考虑网络事务,它可能不仅仅是“锁定 UI 一段时间”,而是“锁定UI 永久”,以防它永远不会加载。

但是,如果您认为这种行为很难看,您可以设置一个计时器(1 或 2 秒),让一些图像有机会加载,如果它们已全部加载或计时器已过期,请继续显示 UI无论如何,使用占位符图像并让其余部分异步加载。无论如何,这将是防止永久锁定的方法。

至于您问题的第一部分,我认为这在某种程度上取决于您显示的图像的上下文和类型。不过,对于大多数网络资产,我认为#2 将是首选方法,因为您当然不想在同一会话中多次下载,但您可能也不想占用永久存储空间。

于 2010-01-13T06:28:15.197 回答
1

关于您的“附带问题”-我认为异步加载它们将是首选行为,特别是因为您需要考虑网络事务,它可能不仅仅是“锁定 UI 一段时间”,而是“锁定UI 永久”,以防它永远不会加载。

为避免这种情况,如果我们通过 droid-fu 讨论WebImageView ,您可以将 ImageLoader.java 的 initialize() 函数更改为此

    static int alive=-1;
   public static synchronized void initialize(Context context) {

        if (executor == null) {        
           executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);           
       }
        else if(alive==executor.getActiveCount()){
               executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
           }
       alive=executor.getActiveCount();
       if (imageCache == null) {
           imageCache = new ImageCacheHope(context, 25, 5);
       }
   }
于 2010-08-22T15:53:15.687 回答
0

我写了一个 Android Image Manager,它可以透明地处理缓存(内存和磁盘)。代码在 Github https://github.com/felipecsl/Android-ImageManager

于 2013-02-12T06:01:02.403 回答