Android Image Compression Upload Foundation

  • 2021-07-18 09:05:10
  • OfStack

In android program development we often see the need to upload pictures of the scene, there is a technical point, the need to compress the picture, and then upload. This can reduce the consumption of traffic and improve the uploading speed of pictures.

How to compress android, the information on the Internet is also a lot, but most of them are code fragments, explaining the compression steps, and there is no practical tool class library. So how to encapsulate the compression algorithm into a utility library? What problems will be encountered, such as:

1. How many pictures do you want to compress

2. Is the compressed picture overwritten or saved to another directory

3. Do you need to delete the original picture if you save the directory

4. If you change the size of the compressed picture, is it reduced according to the scale of the original image or directly specify the size

5. If there is a rotation problem in the original drawing, do you need to correct it

6. Is multi-graph compression concurrent or linear processing

7. Can you use service for compression processing, whether local (local) or remote (remote) to start service

8. If there are many pictures to compress, how to use thread pool to deal with them

Based on the above thoughts, I intend to write a series of articles to solve these problems step by step (forget everyone's continuous attention), and integrate Service, the use of multithreading and compression algorithms into one project. In this way, it is better not only in practical application but also as learning materials. Finally, I will open source the code and iterative process involved in this series to github. Welcome to star and submit bug.

Of course, some friends may say that the number of pictures uploaded at one time in practical application will not be too much. It is a bit too much to consider these problems. Well, if you really think so, you can ignore this series of articles.

In the actual demand, it is basically compressed according to the aspect ratio of the original image, and it is rare to specify the size directly, so this series of articles is also aimed at this equal ratio compression.

In a word, to compress pictures, everyone mainly pays attention to two points:

1. Scale the size of the picture to achieve the purpose of compression

2. Compress the quality of the picture

Scale the size of the picture to achieve the purpose of compression

In view of this situation and the problem of picture rotation, you can refer to my android to deal with the problem of photo rotation and the thinking of memory occupation.

It should be noted that the width and height (actualOutWidth, actualOutHeight) of the final output picture should be calculated according to the aspect ratio (srcRatio) of the original picture, and finally the sampling value sampleSize should be calculated through actualOutWidth and actualOutHeight.

The core code is as follows:


LGImageCompressor.java
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcImagePath, options);
// The width and height of the final output picture are calculated from the aspect ratio of the original picture and the aspect ratio of the desired output picture 
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
float maxWidth = outWidth;// Expected output picture width 
float maxHeight = outHeight;// Expected output picture height 
float srcRatio = srcWidth / srcHeight;
float outRatio = maxWidth / maxHeight;
float actualOutWidth = srcWidth;// Width of final output picture 
float actualOutHeight = srcHeight;// Height of final output picture 
if (srcWidth > maxWidth || srcHeight > maxHeight) {
if (srcRatio < outRatio) {
actualOutHeight = maxHeight;
actualOutWidth = actualOutHeight * srcRatio;
} else if (srcRatio > outRatio) {
actualOutWidth = maxWidth;
actualOutHeight = actualOutWidth / srcRatio;
} else {
actualOutWidth = maxWidth;
actualOutHeight = maxHeight;
}
}
// Calculation sampleSize
options.inSampleSize = computSampleSize(options, actualOutWidth, actualOutHeight); 

To make it easier for everyone to understand the above code, give an extreme example:

If the original picture is srcWidth=40 in width and srcHeight=20 in height. The expected output has a width of maxWidth=300 and a height of maxHeight=10. Then srcRatio=40: 20=2, outRatio=300: 10=30. Obviously srcRatio < outRatio, then the size of our actual final output picture should be based on maxHeight (10), that is, actualOutHeight = maxHeight. Finally, actualOutWidth=actualOutHeight*srcRatio = 10*40/20=20 is calculated according to the ratio of the original picture, and the final actualOutWidth=20 is obtained. The aspect ratio of the final output picture is 20:10=2, which is the same as the aspect ratio of the original picture. Other situations are similar, so we will not explain them in detail here.

Quality compression of pictures

In this case, the API interface in the Bitmap class of android has the compress method


public boolean compress(CompressFormat format, int quality, OutputStream stream)

It should not be difficult to understand the three parameters. You can check the official doc document. The compress method mainly controls the image quality input to stream through quality.

This is more appropriate for the scenario where the space occupied by the picture to be output is not more than 1 fixed value, because we can judge whether the compressed size is greater than the fixed value through loop, and if it is satisfied, reduce quality and continue to execute compress operation. The core code is as follows:


// Perform lossy compression 
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options_ = 100;
actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);// Quality compression method, which stores the compressed data into baos Medium  (100 Indicates that it is not compressed, 0 Indicates compression to minimum )
int baosLength = baos.toByteArray().length;
while (baosLength / 1024 > maxFileSize) {// Loop to judge if the compressed picture is larger than maxMemmorrySize, Greater than Continue Compression 
baos.reset();// Reset baos That is, give way 1 The write of the second time overwrites the content before 
options_ = Math.max(0, options_ - 10);// Every time the picture quality decreases, 10
actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);// Save the compressed picture to the baos Medium 
baosLength = baos.toByteArray().length;
if (options_ == 0)// If the quality of the picture has been minimized, no compression will be performed 
break;
} 

It takes time to compress a super-large graph, so we should consider putting compression into the background thread for execution. If there is no need for high concurrency, AsyncTask can solve the problem.

Core code:


private class CompressTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
return compressImage();// Perform a compression operation 
}
@Override
protected void onPreExecute() {
if (compressListener != null) {
compressListener.onCompressStart();// Listen for callbacks (start compression) 
}
}
@Override
protected void onPostExecute(String imageOutPath) {
if (compressListener != null) {
compressListener.onCompressEnd(imageOutPath);// Listen for callback (end of compression) 
}
}
} 

Appropriately encapsulated code can be implemented by executing in Activity


LGImgCompressor.getInstance(this).withListener(this).starCompress(Uri.fromFile(imageFile).toString(),outWidth,outHeight,maxFileSize);

To start the compression task

Write at the end

In order to achieve the best compression results, the above two schemes can be carried out simultaneously. If compression takes a long time, you need to put the compression process into a background thread to execute.

I wrote a simple demo program to achieve the following functions:

1. Turn on the camera to take photos

2. Specify where to store photos

3. Compress photos to the specified directory

4. Use AsyncTask to perform compression operations

5. Display compressed photos and related information to the foreground activity

Because this version uses AsyncTask asynchronous task to execute compress, and AsyncTask due to android version split problem, some versions are multi-threaded, some versions are single-threaded, and they are drunk. In short, this version is suitable for one compression task is not a lot. If you need to deal with large data compression tasks, you need to consider using thread pool to deal with it.

In addition, how to combine service with multithreading will be explained in detail in the next article.

The demo open source github address is as follows:

LGImageCompressor

The above is the site to introduce the Android picture compression upload of the basic piece of knowledge, I hope to help you, if you want to know more information please pay attention to this site website!


Related articles: