In the development, there will be the requirement of uploading user profile picture. In order to save traffic, the general uploading interface will limit the size of picture, such as 1M. However, as mobile phones become more and more pixelated, this limit can be easily reached, so the client needs to compress the image before uploading it.

We use Bitmap#compress to compress image files. The first step is to create a File object according to the File path. Then determine the size of the File object. If the size is larger than 1M, start compression. ② Then use bitmapFactory. Options to get the width and height of the image file, first halve the width and height of the image; Then create a new Bitmap object according to the reduced width and height. Here, the image data is written to the new Bitmap through Canvas. The data of the new Bitmap is then written to the ByteArrayOutputStream using the Bitmap#compress method. (3) Determine the size of the ByteArrayOutputStream. If it still exceeds the target size, halve the width and height of the image and repeat step 2. ④ Write ByteArrayOutputStream to the file after compression.

Without further ado, go directly to the code:

/ * * * * * under compressed image to the target size @ param file * @ param targetSize * / public void compressBmpFileToTargetSize (file file, long targetSize) { Log.d(TAG, String.format("compressBmpFileToTargetSize start file.length():%d", file.length()));
    if(file.length() > targetSize) {int ratio = 2; Bitmapfactory.options Options = new bitmapFactory.options (); Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options); int targetWidth = options.outWidth / ratio; int targetHeight = options.outHeight / ratio; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int quality = 100; Bitmap result = generateScaledBmp(bitmap, targetWidth, targetHeight, baos, quality); // Count protection to prevent too many times too time-consuming. int count = 0;while(baos.size() > targetSize && count <= 10) { targetWidth /= ratio; targetHeight /= ratio; count++; // reset, otherwise it accumulates baos.reset(); result = generateScaledBmp(result, targetWidth, targetHeight, baos, quality); } try { FileOutputStream fos = new FileOutputStream(file); fos.write(baos.toByteArray()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } } Log.d(TAG, String.format("compressBmpFileToTargetSize end file.length():%d", file.length())); } /** * the image is cut in half ** @param srcBmp * @param targetWidth * @param targetHeight * @param baos * @param quality * @return
 */
private Bitmap generateScaledBmp(Bitmap srcBmp, int targetWidth, int targetHeight, ByteArrayOutputStream baos, int quality) {
    Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(result);
    Rect rect = new Rect(0, 0, result.getWidth(), result.getHeight());
    canvas.drawBitmap(srcBmp, null, rect, null);
    if(! srcBmp.isRecycled()) { srcBmp.recycle(); } result.compress(Bitmap.CompressFormat.JPEG, quality, baos);return result;
}
Copy the code

The Bitmap#compress method is used to write the new Bitmap into the ByteArrayOutputStream, and the data in the ByteArrayOutputStream is already reduced. The reduction is not easy to control, will not be just less than 1M, here just selected a more effective way.

Of course, the compression operation needs to be placed in the child thread, the specific code is as follows:

Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(ObservableEmitter<String> emitter) throws Exception {
        try {
            File f = new File(avatarFileUrl);
            compressBmpFileToTargetSize(f, 1024 * 1024);
            emitter.onNext(avatarFileUrl);
            emitter.onComplete();
        } catch (Exception e) {
            e.printStackTrace();
            emitter.onError(new Throwable("Failed to get picture")); } } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<String>() { @Override public void onSubscribe(Disposable d) { compositeDisposable.add(d); } @override public void onNext(String s) {Override public void onError(Throwable e) { ToastUtil.show(mContext, e.getMessage()); } @Override public voidonComplete() {}});Copy the code

Finally, don’t forget to deal with RxJava memory leaks:

@Override
protected void onDestroy() {
    super.onDestroy();
    compositeDisposable.dispose();
}
Copy the code

Of course, there is no picture file path compatibility processing, read and write permission processing, these need readers to achieve.