Modern phone cameras are incredible, but they have a massive side effect: file size. A single photo taken on a mid-range Android phone can easily range from 6MB to 15MB. While that detail is great for printing posters, it is complete overkill for sharing on chat apps, uploading to a blog, or storing on your phone.
As my device storage filled up and my cloud storage warnings started popping up, I realized I needed a quick way to shrink my photos. I looked at web-based tools, but I didn't feel comfortable uploading private family photos to random servers just to resize them. I looked at existing apps, but they were bloated with ads, trackers, and demanded internet access.
So, I decided to build ImageSlim Free, an offline-first Android app designed to solve this exact problem: shrinking image file sizes by up to 90% without sacrificing visible quality.
Here is how I built it, the technical hurdles I faced, and what I learned along the way.
The Core Problem: Mobile Storage and Bandwidth Bloat
When you share a 10MB photo over a weak mobile connection, it takes forever. If you run a self-hosted blog, uploading multiple 10MB images destroys your page load speeds and hikes up your hosting bills. The core value of ImageSlim is simple: let users select one or multiple images, scale them down, compress the bytes, and save them—all in a few seconds, and entirely on-device.
Technical Challenges of On-Device Processing
Processing high-resolution images on Android is notoriously difficult due to memory limitations.
- The OutOfMemory (OOM) Trap: If you load a 108 megapixel photo directly into Android's memory as a Bitmap, it can require hundreds of megabytes of RAM. Android will instantly kill your app process if you exceed the heap limit.
- UI Thread Blocking: Image compression is CPU-intensive. Running it on the main thread freezes the user interface, causing the system to throw an "Application Not Responding" (ANR) dialog.
To tackle the OOM issue, I used sub-sampling. Instead of loading the full-resolution image into memory just to resize it, I used BitmapFactory.Options with inJustDecodeBounds = true to query the image dimensions first. Once I knew the original size, I calculated an appropriate inSampleSize to decode a downscaled version directly into memory, dramatically reducing the RAM footprint.
To keep the app responsive, I wrapped the entire compression workflow inside Kotlin Coroutines, specifically utilizing Dispatchers.Default for CPU-bound tasks. This keeps the UI buttery smooth even when processing a batch of images.
The Tech Stack
I wanted the project to be lightweight and modern:
- Language: Kotlin
- UI Framework: Jetpack Compose. This allowed me to build a clean, minimal interface without the boilerplate of XML layouts.
- Concurrency: Kotlin Coroutines and Flow to manage background processing states.
-
Graphics Pipeline: Native Android Graphics libraries (
android.graphics.Bitmap), utilizing JPEG/WEBP compression algorithms.
Lessons Learned
Building this app taught me a lot about native memory management. Unlike Java objects, Bitmaps in older Android versions allocated memory in the native heap, but even in modern versions, garbage collection behavior can be unpredictable during heavy image manipulation. Explicitly calling bitmap.recycle() and ensuring references are cleared as soon as the file is written made a massive difference in stability.
Additionally, I learned that restricting your app's capabilities can actually be a feature. By choosing not to request the Internet permission (android.permission.INTERNET) in the manifest, I made it impossible for the app to send data anywhere. This built immediate trust with privacy-conscious users.
Try It Out
If you're tired of running out of phone space or waiting for photo uploads to finish, you can download ImageSlim Free on Google Play.
For more details on the project and other tools, feel free to check out the portfolio page at imageslim.getinfotoyou.com. Let me know your thoughts or if you have any questions about the implementation!
Top comments (0)