It is almost impossible to find a commercial Android application that does not use images in its interface. Downloading a picture into the corresponding View, despite the seeming simplicity of the operation, can cause big trouble for the developer. For example, what if the image is stored on the server? To display it on the screen, you should upload it to the local storage first, then convert it to the desired format, and add it as an attribute in the View. The list of tasks will expand significantly if we want to reuse the already uploaded images or display the placeholder while the desirable image is being downloaded. Therefore, the vast majority of Android projects use special libraries – image downloaders that allow you to break this endless cycle. The most common ones for 2020 are Picasso, Glide, and Fresco.
Was created by Square, which is known to the Android community for such popular libraries as Retrofit, OkHttp, and Leak Canary. The creators of this library put the main emphasis on the simplicity and comprehensibility of the work. The minimalistic approach makes it possible not to inflate the size of the apk file – Picasso takes only 121 Kb. The library also saves the number of methods in the application, adding only 849 of its methods. This puts off the moment of turning the application into a multidex file.
Is Bump Tech’s product. One of the prominent features of the library is its powerful functionality that allows you to implement complex image transformations. However, they come with a price – a bigger application: Glide adds 440 Kb and 2678 methods. Therefore, the Glide niche in the development of Android applications is a program with complex image display logic. However, it is also quite possible to implement simpler solutions with its help.
Was created by Facebook engineers. When loading images, this library focuses on working efficiently with memory and productivity. Especially significant was its advantage over other image downloaders on devices with Android 4. Now the gap has narrowed, but Fresco still continues to lead in this setting. The second significant feature of Fresco is the use of its own SimpleDraweeView instead of the traditional ImageView.
Comparison of the simple functions of these libraries (loading resources, error handling, placeholders) will not bring much value to the Android developer, as these functions are intuitive and easy to learn in just an hour. It would be more interesting to compare the advanced features of Picasso, Glide and Fresco: caching and image transformation, as well as unique tools of each library.
Image caching
Picasso supports 2 types of cache by default: LRU cache size of 15% of the RAM available to the application, and disk cache size from 5 to 50 MB (depends on the available size of read-only memory). The functions memoryPolicy() and networkPolicy() are used to control them. The first allows you to refuse to access the online cache during image loading (attribute MemoryPolicy.NO_CACHE) or not to save the downloaded image to the cache (attribute MemoryPolicy.NO_STORE). With the help of the second, the disk cache is regulated. Its NetworkPolicy.NO_CACHE and NetworkPolicy.NO_STORE attributes work the same as in MemoryPolicy. Another attribute – NetworkPolicy.OFFLINE – instructs Picasso to download data only from the cache, without accessing the network.
For example, such a request will force Picasso to constantly download images from the network.
1 2 3 4 5 6 7 |
Picasso.get() .load(imageUrl) .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE) .networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE) .into(test_imageview) |
The cache scheme in Glide is more complex. It has 4 levels:
- active resources (displayed now in any View);
- memory cache (data in RAM);
- resource (converted and decoded image);
- Data (raw image saved to disk).
By default, before accessing an external resource, Glide searches for a suitable image at these levels in order. With this approach, the same image can have several versions. For example, we use the same picture both in the list and in the detailed view. But in the first case, we derive its part using the center crop, in the second – we show it completely. The cache will contain 2 versions of this image. To distinguish them, Glide uses keys that include data on the width and height of the picture, the transformations, the added options, the data type, etc. To manage the cache, use the diskCacheStrategy() function, which takes the enum arguments of DiskCacheStrategy. ALL (uses all cache levels and an external resource), DATA (writes raw data to read-only memory), RESOURCE (writes decoded data to read-only memory), NONE (does not cache data in persistent memory).
The previous example from Picasso will look like this:
1 2 3 4 5 6 7 |
Glide.with(this) .load(imageUrl) .diskCacheStrategy(DiskCacheStrategy.NONE) .skipMemoryCache(true) .into(test_imageview) |
Like Picasso, Glide allows you to clear the cache completely or for a specific image. But it also makes it possible to control it more precisely by using the signature() function and custom keys.
The Fresco cache is also hierarchical and has 3 levels:
- Bitmap (decoded images, ready for display or post-processing);
- Encoded memory cache (stores compressed images in the original state in memory);
- Disk (stores compressed images in the original state in the local storage).
The library uses the ImagePipeline class to manage the cache. It provides the ability to check the presence of images in the cache, get cached images, or delete them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
val imagePipeline = Fresco.getImagePipeline() // Check if image is in cache val inMemoryCache = imagePipeline.isInBitmapMemoryCache(uri) // Get cached image imagePipeline.evictFromMemoryCache(uri) imagePipeline.evictFromDiskCache(uri) // Clear cache imagePipeline.clearMemoryCaches() imagePipeline.clearDiskCaches() |
An interesting feature of Fresco is the ability to create a separate cache for storing small images. This can be useful for increasing application performance.
Image transformation
One of the main advantages of using downloaders is the ability to process images before output to the user. Picasso offers a set of built-in simple transformations: resize, centerCrop, centerInside, rotate. For more complex changes, you can use ready-made solutions from free libraries. For example, a solution from Daichi Furiya (Wasabeef). With it, you can easily set the complex shape of the picture, blur, or apply masks and filters. There is also the option to create your own custom transformation. To do this, you need to inherit from the Transformation class and prescribe the logic for changing the image. It will also be necessary to set a unique key-string for this transformation, which will allow us to distinguish between processed pictures in the cache.
Circle Transformation Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class CircleTransformation : Transformation { override fun transform(source: Bitmap): Bitmap { val paint = Paint() paint.setAntiAlias(true) paint.setShader(BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)) val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888) val canvas = Canvas(output) canvas.drawCircle(source.width / 2, source.height / 2, source.width / 2, paint) if (source != output) source.recycle() return output } override fun key(): String { return "circle" } } |
Work with transformations in Glide is very similar to that in Picasso. You can take the built-in options for changing images (the same as Picasso plus circleCrop, roundedCorners, granularRoundedCorners). For more complex cases, there are free libraries: from the already mentioned Daichi Furiya (Wasabeef) or from Werbhelius. If necessary, you can always define your own transformation. Its implementation is slightly different from Picasso. First of all, the Transformation interface is most often implemented not directly, but with the help of utility classes, each of which is responsible for transforming a certain type of resource: DrawableTransformation, BitmapTransformation, GifDrawableTransformation, MultiTransformation, etc. To create your transformation, you need to redefine 4 methods: equals() and hashcode() – to distinguish between transformation objects, transform() – actually describes the change in the picture, updateDiskCacheKey() – allows you to identify the changed pictures in the cache.
Custom Transformation Example in Glide
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class GreyscaleTransformation : BitmapTransformation() { override fun transform( pool: BitmapPool, source: Bitmap, outWidth: Int, outHeight: Int ): Bitmap { val bitmap = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888) val paint = Paint() val greyMatrix = ColorMatrix() greyMatrix.setSaturation(0.0f) paint.colorFilter = ColorMatrixColorFilter(greyMatrix) val canvas = Canvas(bitmap) canvas.drawBitmap(source, 0f, 0f, paint) return bitmap } override fun equals(other: Any?): Boolean { return other is GreyscaleTransformation } override fun hashCode(): Int { return Util.hashCode(ID.hashCode()) } override fun updateDiskCacheKey(messageDigest: MessageDigest) { messageDigest.update(ID_BYTES) } companion object { private val ID = "GreyscaleTransformation" private val ID_BYTES = ID.toByteArray() } } |
Among the 3 downloaders in question, Fresco has the most sophisticated image transformation tools. Most of them can be applied in the xml layout, indicating the corresponding attributes. From the coding perspective, applying them is harder. For example, this is how rounding of corners looks when using Fresco.
1 2 3 4 5 6 7 8 |
val roundingParams = RoundingParams.fromCornersRadius(7f) fresco_imageview.setHierarchy( GenericDraweeHierarchyBuilder(resources) .setRoundingParams(roundingParams) .build() ) |
In addition, some standard tools have limitations. For example, resize can only be applied to JPEG format, it cannot make an image larger and can reduce it only to â…› original size.
Like other loaders, Fresco can be used with libraries of ready-made transformations (from the same Wasabeef ).
Creating your transformation requires a custom implementation of the Postprocessor class. Let’s rewrite the example of gray scale with Glide for Fresco.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class GrayScaleProcessor : BasePostprocessor() { override fun process(bitmap: Bitmap) { val w = bitmap.width val h = bitmap.height val pixels = IntArray(w * h) bitmap.getPixels(pixels, 0, w, 0, 0, w, h) for (x in 0 until w) { for (y in 0 until h) { val offset = y * w + x pixels[offset] = getGreyColor(pixels[offset]) } } bitmap.setPixels(pixels, 0, w, 0, 0, w, h) } override fun getName(): String { return "GrayScaleProcessor" } override fun getPostprocessorCacheKey(): CacheKey? { return SimpleCacheKey("grayScale") } companion object { internal fun getGreyColor(color: Int): Int { val alpha = color and -0x1000000 val r = color shr 16 and 0xFF val g = color shr 8 and 0xFF val b = color and 0xFF val luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b).toInt() return alpha or (luminance shl 16) or (luminance shl 8) or luminance } } } |
Unique features
Since Picasso was based on the principle of “nothing more,” it added most of the new features later than its competitors, primarily Glide.
Glide has long been a pioneer in adding functionality to image downloaders on Android. At the moment, you can highlight its most useful features:
- loading a frame from a video as a picture;
- use of any type of model instead of Uri / String for custom loader;
- GIF download (also available in Fresco);
- flexible API with the ability to connect any network stack (for example, Volley or OkHttp).
Fresco was created as an alternative approach to uploading images to Android with an eye on efficiency. Therefore, its main features are hidden under the hood:
- saving images not in Java Heap, but in ashmem heap, which saves memory for applications, reduces the risks of OutOfMemoryError and increases performance due to more rare calls of garbage collector;
- cropping images around any point, not only in the center;
resizing JPEG using native resources, which also reduces the risks of OutOfMemoryError; - support for progressive JPEG images.
Conclusion
Criterion | |||
---|---|---|---|
Size | 121 Kb/td> | 440 Kb | 500 kb |
Convenience of use | high | high | middle |
Web Download Speed | high | slightly lower due to lengthy caching processing | high |
Cache Download Speed | average | high | high |
Built-in Transformation Features | basic set of operations | basic set plus rounding | wide range of transformation capabilities, but with limitations |
Additional tools | Absent. | Loading a frame from video as a picture and GIF, using any type of model, a flexible API with the ability to connect any network stack. | Saving images not in Java Heap, but in ashmem heap, the ability to crop images around any point, resize JPEG using native resources, support for Progressive JPEG images. |
Size | 121 Kb |
Convenience of use | high |
Web Download Speed | high |
Cache Download Speed | average |
Built-in Transformation Features | basic set of operations |
Additional tools | Absent. |
Size | 440 Kb |
Convenience of use | high |
Web Download Speed | Wslightly lower due to lengthy caching processing |
Cache Download Speed | high |
Built-in Transformation Features | basic set plus rounding |
Additional tools | Loading a frame from video as a picture and GIF, using any type of model, a flexible API with the ability to connect any network stack. |
Size | 500 kb |
Convenience of use | middle |
Web Download Speed | high |
Cache Download Speed | high |
Built-in Transformation Features | wide range of transformation capabilities, but with limitations |
Additional tools | Saving images not in Java Heap, but in ashmem heap, the ability to crop images around any point, resize JPEG using native resources, support for Progressive JPEG images. |
Thus, for 2020, Picasso, Glide and Fresco are the most popular libraries for downloading images in Android applications. Each of them has its own specifics of using advanced tools, caching and image transformation in particular. Picasso with a minimalist approach works well with simple image operations, but has limited customization options. Glide is an undisputed leader among Android downloaders, offering powerful functionality and an intuitive interface for its implementation. Fresco’s innovative approach delivers performance gains, but greatly complicates the implementation of caching and image transformation.