Imagine you're traveling, working on a quick web update from your tablet or phone, and a collaborator sends you a zip file of 50 high-resolution PNG screenshots that you need to upload. The platform only accepts WebP or JPG, or maybe the file sizes are simply too massive for a spotty cellular connection.
Uploading these files to an online service is frustrating. You run into upload limits, bandwidth constraints, and the unsettling reality that you're sending potentially sensitive screenshots to an unknown remote server.
This exact scenario is why I built PhotoConvert. I needed a way to batch convert images directly on my Android device—quickly, securely, and entirely offline. Here is how I tackled the engineering challenges of building a local image converter for Android, and what I learned along the way.
Why I Built It: The Mobile Workflow Gap
Most developers and designers have a solid desktop workflow for bulk image conversion. A quick terminal script using ImageMagick or a dedicated desktop app does the job in seconds. But when you move to a mobile device—like an Android tablet or phone—the options thin out.
Existing tools on Google Play were either ad-choked wrappers for web-based APIs or heavy, complex photo editors. I wanted something simple: a utility app that does one thing well. Select images, choose the target format (JPG, PNG, WebP, GIF, or BMP), adjust the quality, and hit convert.
The core rule was that it had to run 100% offline. No servers, no data collection, no telemetry. Just pure local processing.
The Tech Stack
To keep the application responsive and lightweight, I chose a native Android stack:
- Language: Kotlin, which makes asynchronous programming clean and readable.
- UI Framework: Jetpack Compose for a minimalist, intuitive interface.
- Concurrency: Kotlin Coroutines and Flow to manage background conversion tasks without freezing the user interface.
-
Image Processing: Android's native
BitmapandImageDecoderAPIs, coupled with custom file stream handling.
Technical Challenges: Handling Memory on Mobile
Converting a single image is easy. Batch converting 50 large images simultaneously on a mobile device is a quick way to trigger OutOfMemoryError (OOM) crashes. Android allocates a limited heap size to each application, and loading multiple raw Bitmaps into memory at once can exhaust that limit instantly.
To solve this, I implemented several optimizations:
- Sequential Queueing with Coroutines: Instead of processing all images in parallel, which would spike memory usage, the app queues the conversions. I used a custom Coroutine worker pool that processes a limited number of files concurrently (typically scaled to the device's CPU cores, but capped to avoid memory saturation).
-
Streaming and Recycling: Bitmaps are loaded, compressed, and written to the output stream, and then immediately recycled using
bitmap.recycle(). Garbage collection on Android can sometimes be lazy, so explicitly freeing native memory is vital. -
In-Sample Size Scaling: If the user chooses to scale down images to save space, the app reads the image dimensions first (without loading the full pixel data) using
inJustDecodeBounds = true, calculates the scale factor, and loads a downsampled version directly.
Navigating Android's Scoped Storage
Another hurdle was Android’s Scoped Storage system. Accessing files outside the app's private directory requires interacting with the Storage Access Framework (SAF) or using media store APIs.
To make the app user-friendly, I wanted a workflow where users could select images from their system gallery and save them directly to a public folder like Pictures/PhotoConvert. I used MediaStore APIs to write output files, ensuring they immediately show up in the user's system gallery without requiring manual file-syncing apps. Handling URI permissions across different Android OS versions (from Android 10 up to 14) required a fair share of conditional logic and thorough testing.
Lessons Learned
- Local beats cloud for utility: Keeping everything on-device is not only a privacy win, but it's also incredibly fast. Without network latency, local conversion is almost instantaneous.
- Memory is the primary constraint: When building mobile developer utilities, you cannot treat memory as infinite. Profiling memory usage with Android Studio's Profiler was essential to catch leaks early.
- Keep the UI simple: Users want utility apps to get out of their way. Jetpack Compose helped keep the code clean and let me focus on the underlying performance of the conversion engine.
Try It Out
If you have ever found yourself needing to convert image formats on the go, you can try out the app. It's completely free, runs offline, and doesn't track you.
- Google Play Link: https://play.google.com/store/apps/details?id=com.sudarshan.photoconvert
- Web Details: You can find more details and other projects at the site https://photoconvert.getinfotoyou.com.
Let me know what you think, or what features you would like to see added next!
Top comments (0)