Android: displaying animated GIFs
December 31, 2014
Posted by on
As it is known, Android does not have a good support for animated GIFs. It’s a whole story with GIF compression patent owners being greedy and destroying this format for everyone, including themselves. In any case, we need to support it somehow.
- Movie class. I don’t like this approach. It’s some kind of sketchy undocumented class, which does not always work.
- GifDecoder + AnimationDrawable. GifDecoder is a custom class which converts gif frames to bitmaps, and bitmaps are then loaded into AnimationDrawable. Problem with it is that it attempts to decode the entire file at once (that’s how AnimationDrawable works anyway). Which is not necessarily fun when you have 5 Mb gif videos.
- WebView. We used it a lot. WebView has this thing when it usually works, but never ideally. Once you started dealing with it, you’re signing up to handle tons of its stupid quirks, most of which only reproduce on old obscure devices you don’t have. Plus, you can’t use multiple instances in any kind of list views, because it does things to your memory. So your UI options are limited.
All in all, WebView is a simplest approach, which works for most applications. However, I wanted to look into some more possibilities. GifDecoder looked promising, because it was very controllable. It worked fine for smaller animations. Obviously the entire concept was wrong for gif videos, because there’s no way they’ll ever fit anywhere uncompressed, even on disk. So I rewritten this class for streaming and created a GifFileDecoder project here: https://sourceforge.net/projects/giffiledecoder/ The project includes two parts:
- Decoder. Same as GifDecoder, it converts gif frames to bitmaps. Unlike GifDecoder, it only needs to keep one or two frames in memory, so it can play any kind of video (I’ve successfully tested it on 15 Mb 640×640 file). If you want to pre-cache bitmaps, you can still do it with same efficiency, the logic will just be outside the decoder.
- Custom ImageView. It’s a regular ImageView, but it runs decoder in a background thread and does setImageBitmap on itself when needed. It is a somewhat crude approach, it has to be controlled manually, like you have to stop the thread when pausing activity. But there’s also tons of benefits. Like, it’s always clear what’s going on with it, you can pause it at any time and use it as a regular ImageView without any fear of it going rogue and consuming every possible resource.
I have tested the project on my devices, and it appears to run about half as fast as WebView. Which is not great, but also not that bad, considering that Java is not a great language for a decoder, and WebView probably uses some kind of optimization. It runs without delays on newer devices, and starts to lag on heavy videos on something older. But then, by “older” I mean my ultra-budget Samsung phone made in 2011 and running Android 2.3. On that phone, most videos were running on 90 to 100% speed, except some really heavy ones.
All in all, another example of how things are usually really simple once you start looking into them instead of using libraries. GIF format is really not that complex and is well-documented, so it’s not that hard to render manually. C++ would be much more efficient for it, but oh well.
PS: after performance optimizations by Aleksandr Shardakov, decoder runs about twice as efficiently and now shows speeds on par with WebView. Native C++ implementation (see comments) would be even more efficient, but it’s also more difficult to use. So I guess this approach is also not bad.