Specify Uri file image compression, how to avoid OOM?

Posted by itandme on Wed, 03 Jul 2019 19:38:47 +0200

  • Preface

Picture Compression and Secondary Sampling

Why compress pictures?

Imagine: When an App calls the camera of the system to take photos, if we set the data data data from Intent to the picture, it just shows a thumbnail (120x160 width and height), which can't meet our needs. So when we start Activity (), putExtra() specifies a storage path to get Uri. In the case of ARGB_8888, each pixel takes four bytes. A picture usually reaches a level of 3x x x x 4xxx. After calculation, it will reach a level of 60M of tens of Ms, because by default, each App in the system only has 16m of independent memory, which is easy. The memory is blown up, so compressed pictures are very important!

Picture Compression Process

  • core

  • When we load, we use the method with options parameter to load.

  • In options, we specify inJustDecodeBounds as true, only decoding edges, so that pictures will not really be loaded into memory. The basic information of pictures, such as width and height, will be loaded into memory.
  • Compression ratio, sampling ratio value is n power of 2, downward integer
  • Calculate a compression ratio based on the width we want and the width of the image, and set it to options.
  • Close the decode-only edge and decode again

Gets the original graph of the specified path

Transfer value when starting camera

private void takePicSave() {
        Intent intent = new Intent();

        intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
        //Save EXTRA_OUTPUT: Save the captured image to the specified uri path
        mUri = getUri();

        intent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);

        this.startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE_SAVE);

Gets the file for the specified path

private Uri getUri() {

        if(ActivityCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED){

            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                //DCIM
                File root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);

                long time = System.currentTimeMillis();

                File picFile = new File(root,time+".jpg");

                return Uri.fromFile(picFile);
            }else {
                File root = this.getCacheDir();
                long time = System.currentTimeMillis();
                return Uri.parse("file://"+root.getAbsolutePath()+File.separator+time+".jpg");
            }
        }else {
            //Application for Read-Write Memory Card Permission
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                            Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_CODE_EXTERNAL);
        }
        return null;
    }

Image Compression and Secondary Sampling

public Bitmap sampleImage(String filePath){

        BitmapFactory.Options options = new BitmapFactory.Options();
        // Setting the decoding edge only, the picture will not really be loaded into memory. The decoder loads and returns null.
        // But the output field of the picture is assigned.
        // For example, the width and height of the picture
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(filePath, options);

        int outHeight = options.outHeight;
        int outWidth = options.outWidth;

        int scale = Math.max(outHeight / 300, outWidth / 300);
        //scale integrates downward and takes the n power of the true value 2
        options.inSampleSize = scale;

        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(filePath,options);
    }
@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {

            case REQUEST_CODE_TAKE_PICTURE_SAVE:
                //If the Intent data passed by the Photo App is empty, there is no data.
                //Picture display
                //Secondary sampling 
                Bitmap bitmap = sampleImage(mUri.getPath());
                showImage.setImageURI(mUri);
                break;

        }

In this way, the second sampling and compression of pictures can be realized. You can take the memory tool to measure it by yourself. Before compression, it is about 60M, and after compression, it is about 4M, which greatly reduces the memory occupancy. It is also an important optimization of App.

Reprinted please indicate the source