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, contentUris
can 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:
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:
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: