
一个看似宽裕的目标
开始开发 SyncGallery 时,我们定下了一个明确的性能目标:设备上有 10 万个媒体文件时,应用必须依然流畅。我们编写每个算法时都考虑了这个数字,并用大致这个规模的照片库进行测试。
当时,10 万个文件似乎很宽裕。如果每天拍 30 张照片,天天如此,大约需要九年才能达到这个总数。我们确信没有人会在手机上保存比这更多的文件。
用户证明我们错了
首个版本发布后不久,我们就发现这个假设错得有多离谱。真实的照片库远远超出了我们的上限:多年的相机存档、从旧手机和电脑迁移过来的照片,以及存放在大容量存储卡上的整个家庭收藏。许多 Android 手机仍然支持 512 GB 甚至 1 TB 的存储卡——足以容纳比我们最初目标大许多倍的照片库。
应用并没有在第 100,001 个文件时突然崩溃——性能极限很少会这么"礼貌"。相反,本应瞬间完成的操作明显变慢,而且照片库超出目标越多,体验就越差。对于照片库最大的用户——可以说是最需要备份应用的人——这是不够好的。
规模扩大十倍时会发生什么
许多在 10 万个文件时无害的捷径,到了 100 万个文件时就会成为问题。模式总是一样的:某项开销随着照片库的增长而悄悄增加。
- 把所有内容都放在内存中。 一个包含 10 万个条目的列表可以轻松放入内存。规模扩大十倍,再乘以多个界面和缓存,内存压力就会导致卡顿,最坏情况下还会崩溃。
- 只有少数文件变化时却重新检查所有文件。 因为新增了几张照片就重新检查整个照片库,在 10 万个文件时几乎察觉不到。到了 100 万个文件,这可能意味着数分钟的后台工作,却看不到任何好处。
- 每次变化后都更新界面。 在小型照片库中,每处理一个文件就更新一次并不明显。当变化以数万计涌入时,界面可能会卡死。
- 让缓存无限增长。 没有上限的缓存最终会与它本应加速的界面争夺资源。
这些并不是什么罕见的错误。它们只是普通的工程决策,在规模扩大一个数量级后便不再奏效。
为一百万重建
我们将目标提高到 100 万个文件,并以这个数字为新标准重新审视了整个应用。现在,以下几条规则决定着我们开发的一切:
- 照片库增长时保持内存占用平稳。 最重的列表界面现在按窗口从数据库加载文件,而不是把所有内容都放在内存中。大型图片缓存设有上限,会淘汰较旧的条目,而不是无限增长。这项改造正在逐个界面推进;它是我们如今开发时遵循的准则,而不是一条已在各处越过的终点线。
- 比较而不是重读。 扫描会跟踪发生了哪些变化,而不是重新处理所有内容。当云服务商提供变更推送时,我们会使用这些变更,而不是再次列出整个文件夹。
- 绝不让界面等待照片库。 繁重的工作在后台分批运行。界面更新会被合并,因此一次涌入 10,000 项变化不会触发 10,000 次重绘。
- 在真实设备上用真实照片库进行测量。 性能分析取代了猜测。最大的改进来自于不经测量我们永远不会怀疑的地方。
快是一种习惯,而不是一个里程碑
最重要的变化并不是某一项修复,而是让"百万文件"这个问题成为我们评估每项新功能的一部分:当照片库比我们预期的大十倍时,这个界面、这次扫描或这个缓存会怎样?
无论我们选择什么数字,总会有人超过它,而这一次我们希望提前做好准备。
如果您有一个庞大的照片库,这项工作正是为您而做。如果 SyncGallery 在处理您的收藏时让您觉得慢,请告诉我们——真实的照片库比人工基准测试更能让我们学到东西。
试用 SyncGallery
一款适用于 Android 的图库应用,云同步由您掌控。
在 Google Play 上获取