Android - 存储从网络下载的图像

时间:2010-01-13 06:18:25

标签: android caching storage sd-card

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

  1. 将其保存到SD卡,检查创建ListView时是否存在(在后续请求中)并根据需要重新下载(偶尔更新图像时,如果更改)。
  2. 使用Context.getCacheDir()将其存储在缓存中,但可能会被迫更频繁地重新下载,因为我不能依赖缓存中的图像。
  3. 始终下载并永不存储图像。
  4. 图像文件本身相当小,但我希望有些用户可以下载/存储数十个这样的小图像。哪种方法效果最好,和/或什么是首选方法?

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

4 个答案:

答案 0 :(得分:21)

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

例如:如果您正在下载一些临时的,较少的(较少的远程读取)和大小(较少的内存)并且是Activity的本地内容,那么您应该考虑使用SoftReferences将图像保存在内存中。 SoftReferences可能会导致重新获取,但由于项目数量很少,因此应该可以负担得起。

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

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

请注意,您还可以查看使用数据库是否可以提供帮助。See this

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

可以帮助您的示例项目:

Romain Guy的Shelves项目使用两级缓存,其中他使用内存缓存(包含SoftReferences的HashMap)和Sdcard上的存储。Browse Source code here

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

祝你好运!

答案 1 :(得分:3)

关于你的“副问题” - 我认为异步加载它们是首选行为,特别是因为你需要考虑网络事务,它可能不只是“锁定UI一段时间”,但“永久锁定用户界面”以防万一从未加载。

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

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

答案 2 :(得分:1)

  

关于你的“副问题” - 我认为异步加载它们是首选行为,特别是因为你需要考虑网络事务,它可能不只是“锁定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);
       }
   }

答案 3 :(得分:0)

我写了一个Android图像管理器,它透明地处理缓存(内存和磁盘)。代码在Github上https://github.com/felipecsl/Android-ImageManager