A look at the best android image compression libraries and how to use them.

1. airatlovesmusic/Kompressor

Unfortunately, most of compression libs still work with filesystem and don't support contentUris. It can cause problems with new SDK versions, no matter if you have a android:requestLegacyExternalStorage=”true" in manifest or not.
Remember, contentUriscan point not only to local files, but also to "virtual" files on the cloud. Therefore, this lib is built on input and output streams and it doesn't work with files.

Step 1: Install it

Install it as shown below:

dependencies {
    implementation 'com.github.airatlovesmusic:Kompressor:0.2.0'
}

Step 2: Compress Image

Compress your image as shown below:

val inputStream = context.contentResolver.openInputStream(contentUri)
val compressedData = imageCompressor.compress(
        inputStream = context.contentResolver.openInputStream(contentUri),
        format = Bitmap.CompressFormat.JPEG,
        quality = 70
)
val compressedBitmap = BitmapFactory.decodeStream(compressedData)

Do whatever you want with received OutputStream, for example you can make Retrofit RequestBody with bytes from stream.

Configuration!

Let's compress something, but with configuration!

val inputStream = context.contentResolver.openInputStream(contentUri)
val compressedData = imageCompressor.compress(
        inputStream = context.contentResolver.openInputStream(contentUri),
        format = Bitmap.CompressFormat.PNG,
        quality = 70,
        steps = resolutionCompression(outHeight = 720, outWidth = 1024) + sizeCompression(FILE_MAX_SIZE)
)
val compressedBitmap = BitmapFactory.decodeStream(compressedData)

This library has multiple compression constraints you can combine:

  • resolutionCompression (set up required height and width of result image),
  • sizeCompression (set up max file size of image),
  • rotationCompression (rotate image)
  • Custom Configuration

Let's compress something, but with custom configuration!

val inputStream = context.contentResolver.openInputStream(contentUri)
val compressedData = imageCompressor.compress(
        inputStream = context.contentResolver.openInputStream(contentUri),
        format = Bitmap.CompressFormat.PNG,
        quality = 70,
        steps = resolutionCompression(outHeight = 720, outWidth = 1024) + { bm: Bitmap -> bm }
)
val compressedBitmap = BitmapFactory.decodeStream(compressedData)

You can also easily add your own custom compression steps by passing function which takes Bitmap and returns Bitmap.

Reference

Read more here

Or:

Read Individually.

2. jeziellago/image-minifier

A Minifier is a lightweight (21KB) android library for image resizing, format changing and quality focusing in reduce file size.

Step 1: Install it

Add to Project-level build.gradle:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Add to Module-level build.gradle:

dependencies {
    implementation 'com.github.jeziellago:image-minifier:0.1.1'
}

Step 2: Compress Image

With an image file, apply one or multiples transformations:

MinifierFactory.create(context)
    .withImage(originalFile)
    .addTransformations {
        resize(1200, 720)
        convertTo(CompressFormat.JPEG)
    }
    .minify {
        onSuccess { minified -> /* success */ }
        onFailure { error -> /* failure */ }
    }

or use coroutines:

val minifiedFile: File = MinifierFactory.create(context)
    .withImage(originalFile)
    .addTransformations {
        resize(1200, 720)
        convertTo(CompressFormat.JPEG)
    }
    .minify(Dispatchers.IO)

Step 3: Apply Transformations

Resize:

resize(1200, 720)

Format:

convertTo(CompressFormat.JPEG)

Gray scale:

colorGrayScale()

Quality


quality(80)

Reference

Read more here.

Or:

Read Individually.

3. zetbaitsu/Compressor

Compressor is a lightweight and powerful android image compression library.

Compressor will allow you to compress large photos into smaller sized photos with very less or negligible loss in quality of the image.

Follow the following steps:

Step 1: Install it:

dependencies {
    implementation 'id.zelory:compressor:3.0.1'
}

Step 2: Compresss

To Compress Image File:

val compressedImageFile = Compressor.compress(context, actualImageFile)

To Compress Image File to specific destination:

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    default()
    destination(myFile)
}

Customize Compressor

Using default constraint and custom partial of it:

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    default(width = 640, format = Bitmap.CompressFormat.WEBP)
}

Full custom constraint

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    resolution(1280, 720)
    quality(80)
    format(Bitmap.CompressFormat.WEBP)
    size(2_097_152) // 2 MB
}

Using your own custom constraint:

class MyLowerCaseNameConstraint: Constraint {
    override fun isSatisfied(imageFile: File): Boolean {
        return imageFile.name.all { it.isLowerCase() }
    }

    override fun satisfy(imageFile: File): File {
        val destination = File(imageFile.parent, imageFile.name.toLowerCase())
        imageFile.renameTo(destination)
        return destination
    }
}

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    constraint(MyLowerCaseNameConstraint()) // your own constraint
    quality(80) // combine with compressor constraint
    format(Bitmap.CompressFormat.WEBP)
}

Compressor now is using Kotlin coroutines!

Calling Compressor should be done from coroutines scope:

// e.g calling from activity lifecycle scope
lifecycleScope.launch {
    val compressedImageFile = Compressor.compress(context, actualImageFile)
}

// calling from global scope
GlobalScope.launch {
    val compressedImageFile = Compressor.compress(context, actualImageFile)
}

To Run Compressor in main thread:

val compressedImageFile = Compressor.compress(context, actualImageFile, Dispatchers.Main)

Reference

Read more here.

Or:

Read Individually.