DEV Community

Getinfo Toyou
Getinfo Toyou

Posted on

How I Built a Batch Image Compressor for Android That Handles 500 Photos at Once

The Problem That Started It All

A photographer friend of mine had a familiar complaint: she'd shoot 400+ RAW-converted JPEGs at an event, and getting them web-ready meant either a slow desktop workflow or uploading massive files directly to her clients. She was constantly tethered to her laptop for a task that felt like it should be solvable on her phone.

That conversation is what pushed me to build ImageSlim Pro — a professional-grade image compression and batch processing tool for Android.


The Use Case That Shaped Everything

Imagine you're a real estate photographer. You shoot 300 photos of a property, transfer them to your Android device, and need to deliver a web-optimized folder to the agent before you leave the driveway. You don't want generic auto-compression. You need to hit a specific file size range, maintain consistent quality across the batch, and output to a particular format — maybe WebP for a modern site, or JPEG at a defined quality level for an older CMS.

This scenario drove every design decision. It's not about compressing one photo. It's about compressing all of them, with precision, in under a minute.


The Tech Stack

  • Language: Kotlin
  • Image processing: Android's native BitmapFactory and Bitmap.compress(), supplemented with custom quality-stepping logic
  • Format conversion: Handled via encoder selection at the compression stage (JPEG, PNG, WebP lossy/lossless)
  • UI: Jetpack Compose for a clean, responsive interface
  • File handling: Scoped storage with SAF (Storage Access Framework) to support modern Android versions
  • Threading: Kotlin Coroutines with a custom dispatcher pool for parallel batch processing

The Hard Parts

1. Predictable Output File Sizes

This sounds simple until you try it. JPEG quality settings are not linear. Quality 80 might give you a 200KB file on one image and a 900KB file on another, depending on the photo's complexity. I ended up implementing a binary search loop — compress at a target quality, check output size, adjust, repeat — to land within a user-defined file size target. It adds a small processing overhead but gives users the predictability they actually need.

2. Batch Processing Without Killing the UI

Processing 500 images sequentially on the main thread is an obvious disaster. But naively spinning up 500 coroutines in parallel would also hammer memory and cause OOM crashes on mid-range devices. The solution was a bounded coroutine pool — a Semaphore-limited dispatcher that processes N images concurrently (tuned based on available heap) while keeping the UI responsive and showing real-time progress.

3. Scoped Storage on Android 10+

Scoped storage broke a lot of older file-handling patterns. Working with SAF (Storage Access Framework) is more verbose and less intuitive than classic File APIs, but it's necessary for proper permission handling on modern Android. I spent more time on this than I'd like to admit. The key takeaway: persist your URI permissions explicitly with takePersistableUriPermission() or you'll lose access after a reboot.

4. WebP Compatibility

WebP support on Android is fragmented. Lossy WebP is available from API 17+, but lossless and transparency support requires API 18+. I added runtime version checks and gracefully fall back or inform users when a format option isn't supported on their device.


Lessons Learned

Target a specific workflow, not a general one. The clearest feedback I got from beta testers was about concrete scenarios — real estate photographers, e-commerce sellers prepping product photos, bloggers batching header images. Building around those use cases made the feature set coherent instead of sprawling.

File size targeting is a killer feature that most tools ignore. Users don't think in "quality percentage" — they think in "I need all images under 500KB." Bridging that gap was worth the engineering effort.

Coroutine-based parallelism is excellent for media work — but set your bounds carefully. Unbounded parallelism on image data will eat your memory budget fast.


Try It Yourself

If you work with images on Android regularly, give ImageSlim Pro on Google Play a shot. There's also more info at imageslim.getinfotoyou.com.

Happy to answer questions about any of the implementation details in the comments — especially around the batch processing architecture or scoped storage pain points, since those tripped me up the most.

Top comments (0)