Efficient Android Images
--
This article will explain the journey of an image from a developer point of view. Starting when you receive an image from the designer until it’s display in the most optimised way.
This article is following the talk I made during a Singapore Kotlin User group meetup at Google Singapore. Slides and video are available bellow.
Before adding an image to your app
Before adding your image to your application you can apply some optimisations. There are a bunch of tools available online that can do that. However in most cases it’s a lossy compression. Feel free to use any of those:
PNGQuant, ImageMagick , PNGGauntlet, PNGOut, PNGCrush, OptiPNG, CryoPNG, PNG Compressor, Yahoo Smush.it, PNGOptimizer, PunyPNG, TinyPNG, PNGWolf, Advpng, DeflOpt, Defluff, Huffmix, TruePNG, PNGnq-s9, Median Cut Posterizer, scriptpng, pngslim, zopfliPNG
How it works
All those services are pretty much applying the same algorithm which are doing as follows.
- Reducing Colors: Reducing the number of unique colors in your image
- Filtering: Reduce dynamic by how variant adjacent pixel colors are to each other.
- Deflate: Remove redundant information
- Indexed images: Chooses the best 256 colors to use, and replaces all your pixels with an index into that color palette.
After optimisation, your image can be reduce from few Mb to few Kb. This will reduce the impact of your image on your APK size.
Memory footprint for a full screen image
Bitmap can be seen as a 2 dimensional array of bit. If we take the case of a full screen image on a Full HD device.The footprint can be calculated this way:
- Screen width = 1920px
- Screen height = 1080px
- Total = 2,073,600px
If we are drawing a black and white image. We only need 1 bit per pixel. So the memory footprint will be 2,073,600 bit (0.26MB)
Sadly not all our images are black and white so we need more data to represent the color of each pixel. On Android. We can choose the precision we would like to have.
Here is a summary table from Android documentation. For each precision. I added the total memory footprint for a full HD image.
I didn’t add the memory footprint when using Hardware. However, it will only use half of the memory compared to RGG_8888 for the exact same result. The system is storing pixel data in graphics memory instead of Java heap. It’s default settings when loading images with Glide.
However, hardware bitmap has some tradeoff. It’s only available starting Android O so it won’t benefit to old / low end devices. Each image will also consume a file descriptor (FD) which can lead to crash.
Different caching mechanisms
2 different caching mechanism can be used for solving 2 different problems. LRUCache and Bitmap Pool.
LRU cache (Least Recently Used)
LRU cache is useful for images that are still used. We can keep a final image and avoid redundant computation in case of resizing for example.
Bitmap Pool
Bitmap Pool is for images that are no longer in use. The memory space allocated by the previous image can be reuse. The system will draw the new image on top of the old image. Starting SDK 19, Bitmap pool support different images dimensions.
This two mechanism will help reducing the pressure on the memory. It will reduce the number of garbage collection (GC) or can even help reducing Out Of Memory crash (OOM).
In short: Keep it simple and use a library
Library like Glide and Fresco implement all those optimisation and that’s the main reason they are so good at it.
Fresco: #1 choice (Created by Facebook)
- Most advanced but complex APIs
Glide: Best compromise (Recommended by Google)
- LRU cache
- Bitmap pool
- Hardware bitmap / Pixel Color configuration
- Gif support
Android Vector Drawable: Differences with SVG and tradeoff
SVG support complex capabilities like Blur, filter effects, Embedding other images, Animated gifs and possibility to executing arbitrary javascript. Supporting everything on Android was not suitable for Android.
Android runs on constrained mobile devices so supporting the entirety of the SVG spec wasn’t a realistic goal. (from: Understanding Android’s vector image format: VectorDrawable) [ Nick Butcher: Android developer at Google]
Android Vector Drawable can be seen as a simpler version of SVG standard. Only basic operation are supported to draw the images. Here is an example of how Android will interpret those information create the render.
- M move to
- L line to
- C (cubic bezier) curve to
- Z close (line to first point)
Parsing and rendering Android Vector Drawable
- Inflation: Parse your AVD file into VectorDrawable modeling the the paths, groups etc you declare
- Drawing: These model objects then have to be drawn by executing Canvas drawing commands.
Steps are proportional to the complexity of the vector and the type of operations you perform. Drawing stage only needs to be performed once and can then be cached to a Bitmap
Vectors provide the aforementioned benefits but at the cost of being more expensive to render.
Optimising SVG and Android Vector Drawable
Before importing SVG to Android to you use Svgo. It can help reduce complexity and remove unnecessary meta information added by editor like photoshop.
After importing the image, you can use Avocado to optimise the Android Vector Drawable.
Note that both library are lossy compression. However from my experience the difference is not noticeable.
Common issues with Android Vector Drawable
AVD are constraints by 3 rules. On Android, Lint warning are implemented and Android Studio will highlight in the following cases. I strongly suggest to follow and fix those warning otherwise you will increase the memory pressure and it can even lead to crash.
- “android:width” : cannot be larger 200dp
- “android:height” : cannot be higher 200dp
- “android:path”: cannot exceed 1000 chars
Learn more about this topic
Here I some great resources you can find to want to dig deeper.
- https://medium.com/@duhroach/reducing-png-file-size-8473480d0476
- https://medium.com/appnroll-publication/what-is-new-in-android-p-imagedecoder-animatedimagedrawable-a65744bec7c1
- https://medium.com/androiddevelopers/understanding-androids-vector-image-format-vectordrawable-ab09e41d5c68
- https://medium.com/androiddevelopers/using-vector-assets-in-android-apps-4318fd662eb9
- https://medium.com/androiddevelopers/draw-a-path-rendering-android-vectordrawables-89a33b5e5ebf
Thanks for reading this article. Be sure to click 👏 below to recommend this article if you found it helpful. It would let others get this article in feed and spread the knowledge.