Building Carousel Part II: Speeding Up the Data Model
Article Summary
Dropbox engineers faced a brutal reality: reading 5,000 photos from SQLite took a full second on a Nexus 5. For users with 100,000+ photos, the standard approach would be unusable.
This deep dive from Dropbox's Carousel team reveals how they built a data model that could handle massive photo libraries without blocking the UI. The solution required rethinking everything from SQLite cursors to in-memory data structures.
Key Takeaways
- Standard Android Cursors rerun queries with OFFSET, causing multi-second hangs on main thread
- Built C++ accumulator model shared across iOS and Android to avoid Java GC stutters
- Immutable snapshots use shallow copies: only changed events get deep copied
- Binary search over event offsets enables fast lookups by absolute photo position
- Transactional interface lets sync, disk reads, and camera roll run on separate threads
By replacing full array copies with shallow event snapshots, Dropbox reduced the cost of hiding a single photo from nearly one second to negligible time, even with 50,000 photos loaded.
About This Article
They needed a carousel that could display photos grouped by events. The tricky part was supporting fast lookups in two different ways: by absolute position in the full list, and by (event_index, index_within_event) pairs. All of this had to work efficiently even with massive datasets.
Dropbox cached event offsets inside immutable snapshots. When looking up a photo by absolute position, they used binary search to find the right event boundary without having to scan through everything.
This approach made photo position queries run in O(log n) time, which meant the UI thread could stay responsive. On iOS, they could swap snapshots atomically using reloadData. On Android, ListAdapter updates worked the same way without blocking.