安卓本地图片缓存解决方案:Square Picasso、Universal Image Loader、Glide、Fresco?

91
我正在寻找一款在 Android 上实现异步图片加载和缓存的库。我本来想使用 Picasso,但是发现 Universal Image Loader 在 GitHub 上更受欢迎。有人了解这两个库吗?能提供一些优缺点的总结吗?
(我的所有图片都存储在本地磁盘上,因此不需要网络功能,所以我认为 Volley 不适用)
5个回答

81

2018年9月更新:几年后,我需要一个本地图像缓存解决方案,它与目前需要的功能几乎相同。这一次,UIL已经没有在积极开发了。我比较了一些流行的库,结论非常显然:只需使用Glide。它更强大且可配置性更高。几年前我不得不分叉并对UIL进行更改。Glide支持所有我的用例,包括有关缓存策略和具有自定义键的多个分辨率缓存的所有内容。只需使用Glide!

Koushik Dutta的比较主要是针对速度基准测试的。他的帖子只涉及非常基本的内容,并且不特定于本地图像。我想分享一下我在问问题后使用Picasso和UIL的经验。Picasso和UIL都可以加载本地图像。我首先尝试了Picasso并感到满意,但后来我决定切换到UIL以获得更多的自定义选项。

Picasso:

  • Picasso的流畅接口很好。但是跳来跳去的“with”、“into”、“load”,你实际上不知道背后发生了什么。返回的结果让人困惑。

  • Picasso允许您指定精确的目标大小。当您面临内存压力或性能问题时,您可以为速度牺牲一些图像质量。

  • 图像缓存时带有尺寸,这对于显示不同尺寸的图像非常有用。

  • 您可以自定义内存缓存大小。但是它的磁盘缓存仅用于http请求。对于本地图像,如果您关心加载速度,则最好拥有缩略图磁盘缓存,这样您不必每次读取几MB的图像。Picasso没有此机制来调整大小并保存缩略图到磁盘。

  • Picasso不公开访问其缓存实例。(您可以在第一次配置Picasso时获得它并将其保留下来…)。

  • 有时您想要异步地将图像读入由侦听器返回的位图中。令人惊讶的是,Picasso没有这个功能。“fetch()”不传递任何东西。“get()”用于同步读取,“load()”用于异步绘制视图。

  • Picasso在主页上只有几个简单的例子,您需要阅读无序的javadoc才能获得高级用法。

  • UIL:

    • UIL使用构建器进行自定义。几乎所有内容都可以配置。

    • UIL不允许您指定要加载到视图中的大小。它使用基于视图大小的一些规则。它不像Picasso那样灵活。我没有办法加载较低分辨率的图像以减少内存占用。(编辑:通过在源代码中添加ImageSize参数并绕过视图大小检查,可以轻松修改此行为)

    • UIL提供可定制的磁盘缓存,您可以使用此功能来缓存指定大小的缩略图。但它并不完美。这里是详细信息:details。(编辑:如果您关心速度并且想要多个级别的缩略图缓存(例如我的情况),则可以修改源代码,使磁盘缓存使用“memoryKey”,并使其也具有大小敏感性)

    • UIL默认情况下会将不同大小的图像缓存在内存中,并且可以在配置中关闭。

    • UIL公开了您可以访问的后备内存和磁盘缓存。

    • UIL提供了灵活的方式,您可以获取位图或加载到视图中。

    • UIL在文档方面更好。 UIL在Github页面上提供了详细的用法说明,并提供了链接教程。

    如果需要更多的控制和自定义,请从Picasso开始,使用UIL。


    我实际上被两者之间卡住了... 我基本上要从存储在那里的服务器中的目录中返回图像... 因此通过http调用,然后将其存储到缓存中(缩略图和常规大小,我可能会在我的目录中存储两种尺寸)... 那么picasso是正确的选择吗? - Lion789
    @Lion789 Picasso仅为本地文件提供本地内存缓存,并使用HttpResponseCache进行网络磁盘缓存,您需要深入了解。UIL具有可配置的磁盘缓存,您可以进行一些小的更改,以使其接受不同大小的图像/缩略图。也许先尝试Picasso,如果发现它太有限,可以选择UIL并自定义。 - X.Y.
    这样Picasso就可以加载更小的图片了!那我就不必加载800万像素的图片了!谢谢,你帮了我一个大忙! - Aron Lorincz
    你能回答这个问题吗? http://stackoverflow.com/questions/35433895/android-picasso-misses-pictures-sometimes - Usman Rana
    UIL 不允许您指定要加载到视图中的大小,并不完全正确。使用 UIL,您可以使用public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) - Martin Mlostek
    并且"UIL"是轻量级的。 - Sumit

    72

    如果你在G+上阅读了Koush的this帖子,你将会得到明确的解决方案,我已经总结了其中的内容。Android-Universal-Image-Loader是符合你要求的最佳选择!

    • Picasso 如果你使用网络,它有最好的图像API!

    • UrlImageViewHelper + AndroidAsync 是最快的。使用这两个伟大的库真正凸显了图像API已经过时。

    • Volley 真是太棒了;我真的很喜欢他们可插拔的后端传输,也许最终会将 AndroidAsync 替换为 Volley。请求优先级和取消管理非常好(如果你使用网络)。

    • Android-Universal-Image-Loader 是目前最流行的。高度可定制。

    这个项目旨在提供一个可重用的工具,用于异步加载、缓存和显示图像。它最初基于Fedor Vlasov的项目,并自那时以来得到了大量重构和改进。
    即将推出的新版本UIL(1.9.2)中的更改:
    - 可以在UI线程之外调用ImageLoader - 新的磁盘缓存API(更灵活)。基于Jake Wharton的DiskLruCache的新LruDiscCache。
    考虑到所有这些,Android-Universal-Image-Loader适合您的需求(在本地磁盘上加载图像)!

    我最初使用的是 Picasso,尽管已经全部实现了,但最终我还是转而使用了 Universal。Picasso 有更好的 API 接口,但也存在很多问题。这个问题让我彻底放弃了 Picasso。 - Lisandro

    45
    我想分享一下我使用过的这三个库的经验:UIL、Picasso和Volley。我之前使用过UIL ,但后来得出结论,我无法真正推荐它,建议改用由高度才华横溢的团队开发的Volley或Picasso,这两个库都不错。虽然UIL也不差,但它缺乏其他两个库的细节注意力。
    我发现UIL在UI性能方面表现不佳;它比Volley或Picasso更容易锁定UI线程。其中部分原因可能是UIL不支持图片响应的批处理,而Picasso和Volley默认情况下都支持批处理。
    此外,我不喜欢UIL的磁盘缓存系统。虽然您可以在各种实现之间进行选择,但我需要指出,目前没有办法通过总大小和实体到期时间来限制UIL磁盘缓存。Volley和Picasso可以做到这一点,并默认使用服务器返回的到期时间,而UIL则忽略它。
    最后,UIL允许您设置全局图像加载器配置,包括所选的磁盘缓存和内存缓存实现以及其他详细信息,但此配置将应用于应用程序中的所有位置。因此,如果您需要更多的灵活性,例如两个单独的磁盘缓存,则对于UIL来说是行不通的。另一方面,Volley允许您拥有任意多个单独的图像加载器,每个都具有自己的配置。Picasso默认使用全局实例,但也允许构建单独可配置的实例。
    总之:Picasso具有最好的API,但它使用全局HTTP磁盘缓存,该缓存在所有HttpURLConnection实例之间共享,在某些情况下可能过于限制。 Volley具有最佳的性能和模块化,但不太用户友好,并且需要编写一个或两个自己的模块才能使其按您希望的方式工作。总体而言,我会推荐对UIL使用这两个库进行替代。
    编辑(2014年12月18日):自我撰写此初始答案以来,情况发生了变化,我觉得有必要对其进行改进:
    Picasso 2.4比旧版更加可配置,当与OkHttp一起使用时(强烈推荐),它还能够为每个实例使用单独的磁盘缓存,因此您可以做任何事情。更重要的是,我注意到 Picasso和OkHttp的性能有了很大的提高,在我看来,现在它是Android上最快的图像加载器解决方案。请注意,在我的代码中,我总是使用.fit().centerCrop().centerInside()相结合,以降低内存使用量并避免在UI线程上调整位图大小。Picasso正在积极开发和支持,这无疑是一个巨大的优势。

    Volley没有太大的变化,但我同时注意到两个问题:

    • 有时在重载时,由于某些磁盘缓存损坏,一些图像不再被加载。
    • 以NetworkImageView(比例类型设置为centerCrop)显示的缩略图相对于其他库获得的缩略图而言相当模糊。

    出于这些原因,我决定停止使用Volley。

    UIL仍然很慢(特别是磁盘缓存),并且其API往往会经常更改。

    我还测试了这个新库叫做Glide 3,它声称比Picasso更优化,并提供了类似于Picasso的API。根据我的个人经验,在网络请求下重载时,它实际上比Picasso和Volley更慢,即使与OkHttp一起使用也是如此。更糟糕的是,在离开活动时会导致我的应用程序崩溃。它仍然比竞争对手有2个优点:

    • 支持动画GIF解码
    • 将最终缩小的位图放入磁盘缓存中,这意味着从磁盘缓存读取非常快。

    结论:我现在推荐使用Picasso + OkHttp,因为它提供了最佳的灵活性、API、性能和稳定性的组合。如果需要GIF支持,您还可以考虑Glide。


    1
    针对您最后提到的UIL问题,您可以创建任意数量的ImageLoader类和配置。您只需要继承ImageLoader类即可。请参见此处:https://github.com/nostra13/Android-Universal-Image-Loader/issues/92#issuecomment-9915691 - TalkLittle
    看起来像是一个hack,但感谢你的提示,这很有用。 - BladeCoder
    3
    我并不完全同意这个观点,我们在这里使用的是 Picasso,我有一个包含500多张高分辨率图片的相册,但是我遇到了性能和内存问题,后来尝试了UIL,问题迎刃而解。这是在一个最小的样本上进行测试得出的结果,成功的解决了我们之前遇到的问题。 - HaMMeReD
    如果您正在显示比屏幕分辨率高得多的图像或许多高分辨率图像的缩略图,则应该对它们进行降采样。我认为UIL会自动执行此操作,而如果您没有指定正确的选项,Picasso则不会执行此操作,因此会出现内存问题。我个人更喜欢在Volley中使用NetworkImageView,它是一个将加载的图像降采样到其自身大小的小部件。 - BladeCoder
    在UIL中,如果我们不想更改或应用其他处理于特定图像,则可以使用DisplayImageOptions类。 - Rahul Rastogi
    我所见过的 Glide 崩溃都是因为我做了一些愚蠢的事情,比如在已销毁的活动或分离的片段上加载图像。 - Teovald

    7
    我已经开发了一款应用程序,应该不断从网络上获取并显示图像。我打算编写一个图像缓存机制,但在此之前,我的朋友向我推荐了通用图像加载器(universal image loader)。UIL非常易于自定义,以至于新手可能会出现错误。然而,UIL在我的应用程序中速度很慢,变得有点慢。我的使用案例是带有图像的ListView。
    昨天我在寻找UIL的替代品时,我发现了Picasso。Picasso易于集成和使用:只需Picasso.context(context).load(url).into(imageview),即可更快,更平稳地集成图像。
    对我来说,Picasso绝对是要使用的API。我的UIL体验不好。

    给未来的读者:Glide比Picasso更好。来看一下吧。 - therealprashant

    0

    我认为ImageLoader比Picasso库更具可定制性和灵活性。


    8
    怎么做?稍作解释会有所帮助。 - Darpan

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接