Many people will use Media.getBitmap in onActivityResult to get the returned pictures when they call the gallery to select pictures, as follows:
Uri mImageCaptureUri = data.getData(); Bitmap photoBmp = null; if (mImageCaptureUri != null) { photoBmp = MediaStore.Images.Media.getBitmap(ac.getContentResolver(), mImageCaptureUri); }
However, Media.getBitmap is not a good way to get known uri images. Let's look at the source code of Media.getBitmap():
public static final Bitmap getBitmap(ContentResolver cr, Uri url) throws FileNotFoundException, IOException { InputStream input = cr.openInputStream(url); Bitmap bitmap = BitmapFactory.decodeStream(input); input.close(); return bitmap; }
In fact, it's very simple and crude. It returns the original size of bitmap. When the picture selected by the gallery is very large, the program is likely to report OOM.
In order to avoid OOM, we need to improve the method, compress the image before BitmapFactory.decodeStream. Here is my improved code:
Call in onActivityResult
Uri mImageCaptureUri = data.getData(); Bitmap photoBmp = null; if (mImageCaptureUri != null) { photoBmp = getBitmapFormUri(ac, mImageCaptureUri); }
/** * Get pictures through uri and compress them * * @param uri */ public static Bitmap getBitmapFormUri(Activity ac, Uri uri) throws FileNotFoundException, IOException { InputStream input = ac.getContentResolver().openInputStream(uri); BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); onlyBoundsOptions.inJustDecodeBounds = true; onlyBoundsOptions.inDither = true;//optional onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional BitmapFactory.decodeStream(input, null, onlyBoundsOptions); input.close(); int originalWidth = onlyBoundsOptions.outWidth; int originalHeight = onlyBoundsOptions.outHeight; if ((originalWidth == -1) || (originalHeight == -1)) return null; //Image resolution is based on 480x800 float hh = 800f;//The height is set as 800f here float ww = 480f;//Set the width here to 480f //Zoom ratio. Because it is a fixed scale, only one data of height or width is used for calculation int be = 1;//be=1 means no scaling if (originalWidth > originalHeight && originalWidth > ww) {//If the width is large, scale according to the fixed size of the width be = (int) (originalWidth / ww); } else if (originalWidth < originalHeight && originalHeight > hh) {//If the height is high, scale according to the fixed size of the width be = (int) (originalHeight / hh); } if (be <= 0) be = 1; //Proportional compression BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = be;//Set scaling bitmapOptions.inDither = true;//optional bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional input = ac.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return compressImage(bitmap);//Mass compression again }
/** * Mass compression method * * @param image * @return */ public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//Quality compression method, here 100 means no compression, store the compressed data in the BIOS int options = 100; while (baos.toByteArray().length / 1024 > 100) { //Cycle to determine if the compressed image is greater than 100kb, greater than continue compression baos.reset();//Reset the BIOS to clear it //First parameter: picture format, second parameter: picture quality, 100 is the highest, 0 is the worst, third parameter: save the compressed data stream image.compress(Bitmap.CompressFormat.JPEG, options, baos);//Here, the compression options are used to store the compressed data in the BIOS options -= 10;//10 less each time } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//Store the compressed data in ByteArrayInputStream Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//Generate image from ByteArrayInputStream data return bitmap; }
The OOM problem has been solved, but another problem has been encountered. The pictures returned after taking photos or selecting photos with Samsung mobile phone have turned 90 degrees, and then changed.
Code improvement in onActivityResult:
Uri originalUri = null; File file = null; if (null != data && data.getData() != null) { originalUri = data.getData(); file = getFileFromMediaUri(ac, originalUri); } Bitmap photoBmp = getBitmapFormUri(ac, Uri.fromFile(file)); int degree = getBitmapDegree(file.getAbsolutePath()); /** * Rotate the picture to a positive direction */ Bitmap newbitmap = rotateBitmapByDegree(photoBmp, degree);
/** * Get file via Uri * @param ac * @param uri * @return */ public static File getFileFromMediaUri(Context ac, Uri uri) { if(uri.getScheme().toString().compareTo("content") == 0){ ContentResolver cr = ac.getContentResolver(); Cursor cursor = cr.query(uri, null, null, null, null);// Find from database according to Uri if (cursor != null) { cursor.moveToFirst(); String filePath = cursor.getString(cursor.getColumnIndex("_data"));// Get picture path cursor.close(); if (filePath != null) { return new File(filePath); } } }else if(uri.getScheme().toString().compareTo("file") == 0){ return new File(uri.toString().replace("file://","")); } return null; }
/** * Read the rotation angle of the picture * * @param path Absolute path of picture * @return Rotation angle of picture */ public static int getBitmapDegree(String path) { int degree = 0; try { // Read the picture from the specified path and obtain its EXIF information ExifInterface exifInterface = new ExifInterface(path); // Get rotation information for pictures int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; }
/** * Rotate the picture at an angle * * @param bm Pictures to rotate * @param degree Rotation angle * @return Rotated picture */ public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) { Bitmap returnBm = null; // Generate rotation matrix according to rotation angle Matrix matrix = new Matrix(); matrix.postRotate(degree); try { // Rotate the original image according to the rotation matrix and get a new image returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); } catch (OutOfMemoryError e) { } if (returnBm == null) { returnBm = bm; } if (bm != returnBm) { bm.recycle(); } return returnBm; }