Android 从Uri加载位图时出现内存不足错误

Android 从Uri加载位图时出现内存不足错误,android,bitmap,Android,Bitmap,我使用以下方法从Uri获取位图: private static Bitmap getBitmapFromUri(@NonNull Context context, @NonNull Uri uri) throws IOException { ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");

我使用以下方法从
Uri
获取
位图

private static Bitmap getBitmapFromUri(@NonNull Context context, @NonNull Uri uri) throws IOException {

        ParcelFileDescriptor parcelFileDescriptor =
                context.getContentResolver().openFileDescriptor(uri, "r");
        assert parcelFileDescriptor != null;
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        parcelFileDescriptor.close();
        return image;
    }
问题是,我有时在
Crashlytics
的return语句中遇到OOM错误。我认为这是因为所选图像的大小较大。如何修改它以返回最佳质量的位图缩小版本,从而不会导致内存不足错误


编辑我自己发布了一个答案。请看一看,让我知道这是正确的方法。

解码前,您必须调整图像大小。这是我解码图像并在图像视图中显示它的代码

private void loadImage(Uri u, String path) {
    try {
        ContentResolver cr = context.getContentResolver();
        InputStream in;
        in = cr.openInputStream(u);
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, o);
        in.close();

        int scale = 1;
        if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
            scale = (int) Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
        }

        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        in = cr.openInputStream(u);
        Bitmap bitmap = BitmapFactory.decodeStream(in, null, o2);
        in.close();
        Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), getRotation(path), true);

        int nh = (int) ( rotatedBitmap.getHeight() * (512.0 / rotatedBitmap.getWidth()) );
        Bitmap scaled = Bitmap.createScaledBitmap(rotatedBitmap, 512, nh, true);
        String pathTest = MediaStore.Images.Media.insertImage(context.getContentResolver(), scaled, "Title", null);
        ImageLoader imageLoader = ImageLoader.getInstance();
        imageLoader.displayImage(Uri.decode(Uri.parse(pathTest).toString()), mPicture, new ImageLoadingListener() {
            @Override
            public void onLoadingStarted(String imageUri, View view) {

            }

            @Override
            public void onLoadingFailed(String imageUri, View view, FailReason failReason) {

            }

            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                mPicture.setImageBitmap(loadedImage);
            }

            @Override
            public void onLoadingCancelled(String imageUri, View view) {

            }
        });
    } catch (Exception e) {
        //Toast.makeText(context, context.getString(R.string.fail_to_load_image), Toast.LENGTH_SHORT).show();
        Log.e("TAG", e.toString());
    }
}

private Matrix getRotation(String pathPetPicture) {
    Matrix matrix = new Matrix();
    try {
        ExifInterface exif = new ExifInterface(pathPetPicture);
        int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);

        switch (rotation) {
            case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                matrix.setScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_ROTATE_180:
                matrix.setRotate(180);
                break;

            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                matrix.setRotate(180);
                matrix.postScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_TRANSPOSE:
                matrix.setRotate(90);
                matrix.postScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_ROTATE_90:
                matrix.setRotate(90);
                break;

            case ExifInterface.ORIENTATION_TRANSVERSE:
                matrix.setRotate(-90);
                matrix.postScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_ROTATE_270:
                matrix.setRotate(-90);
                break;

            case ExifInterface.ORIENTATION_NORMAL:
            default:
                break;
        }
    } catch (Exception e) {
        Log.e("TAG", e.toString());
    }

    return matrix;
}

在解码之前,您必须调整图像的大小。这是我解码图像并在图像视图中显示它的代码

private void loadImage(Uri u, String path) {
    try {
        ContentResolver cr = context.getContentResolver();
        InputStream in;
        in = cr.openInputStream(u);
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, o);
        in.close();

        int scale = 1;
        if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
            scale = (int) Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
        }

        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        in = cr.openInputStream(u);
        Bitmap bitmap = BitmapFactory.decodeStream(in, null, o2);
        in.close();
        Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), getRotation(path), true);

        int nh = (int) ( rotatedBitmap.getHeight() * (512.0 / rotatedBitmap.getWidth()) );
        Bitmap scaled = Bitmap.createScaledBitmap(rotatedBitmap, 512, nh, true);
        String pathTest = MediaStore.Images.Media.insertImage(context.getContentResolver(), scaled, "Title", null);
        ImageLoader imageLoader = ImageLoader.getInstance();
        imageLoader.displayImage(Uri.decode(Uri.parse(pathTest).toString()), mPicture, new ImageLoadingListener() {
            @Override
            public void onLoadingStarted(String imageUri, View view) {

            }

            @Override
            public void onLoadingFailed(String imageUri, View view, FailReason failReason) {

            }

            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                mPicture.setImageBitmap(loadedImage);
            }

            @Override
            public void onLoadingCancelled(String imageUri, View view) {

            }
        });
    } catch (Exception e) {
        //Toast.makeText(context, context.getString(R.string.fail_to_load_image), Toast.LENGTH_SHORT).show();
        Log.e("TAG", e.toString());
    }
}

private Matrix getRotation(String pathPetPicture) {
    Matrix matrix = new Matrix();
    try {
        ExifInterface exif = new ExifInterface(pathPetPicture);
        int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);

        switch (rotation) {
            case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                matrix.setScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_ROTATE_180:
                matrix.setRotate(180);
                break;

            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                matrix.setRotate(180);
                matrix.postScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_TRANSPOSE:
                matrix.setRotate(90);
                matrix.postScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_ROTATE_90:
                matrix.setRotate(90);
                break;

            case ExifInterface.ORIENTATION_TRANSVERSE:
                matrix.setRotate(-90);
                matrix.postScale(-1, 1);
                break;

            case ExifInterface.ORIENTATION_ROTATE_270:
                matrix.setRotate(-90);
                break;

            case ExifInterface.ORIENTATION_NORMAL:
            default:
                break;
        }
    } catch (Exception e) {
        Log.e("TAG", e.toString());
    }

    return matrix;
}
在getBitmapFromUri()中使用此选项并将代码替换为此选项


在getBitmapFromUri()中使用此选项并将代码替换为此选项。

尝试以下操作

 // decode image
        public Bitmap decodeFile(String filePath) {
            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(filePath, o);
            // The new size we want to scale to
            final int REQUIRED_SIZE = 1024;
            // Find the correct scale value. It should be the power of 2.
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true) {
                if (width_tmp < REQUIRED_SIZE && height_tmp < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }

            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            bitmap = BitmapFactory.decodeFile(filePath, o2);            
            return bitmap;
        }
//解码图像
公共位图解码文件(字符串文件路径){
//解码图像大小
BitmapFactory.Options o=新的BitmapFactory.Options();
o、 inJustDecodeBounds=true;
解码文件(文件路径,o);
//我们要扩展到的新尺寸
所需的最终int_SIZE=1024;
//找到正确的刻度值。它应该是2的幂。
内部宽度=o.向外宽度,高度=o.向外高度;
int标度=1;
while(true){
如果(宽度\u tmp<要求的\u尺寸和高度\u tmp<要求的\u尺寸)
打破
宽度_tmp/=2;
高度_tmp/=2;
比例*=2;
}
//用inSampleSize解码
BitmapFactory.Options o2=新的BitmapFactory.Options();
o2.inSampleSize=刻度;
位图=BitmapFactory.decodeFile(文件路径,o2);
返回位图;
}
愿它有帮助


如果您想高效加载大型位图,请按照以下方法操作

 // decode image
        public Bitmap decodeFile(String filePath) {
            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(filePath, o);
            // The new size we want to scale to
            final int REQUIRED_SIZE = 1024;
            // Find the correct scale value. It should be the power of 2.
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true) {
                if (width_tmp < REQUIRED_SIZE && height_tmp < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }

            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            bitmap = BitmapFactory.decodeFile(filePath, o2);            
            return bitmap;
        }
//解码图像
公共位图解码文件(字符串文件路径){
//解码图像大小
BitmapFactory.Options o=新的BitmapFactory.Options();
o、 inJustDecodeBounds=true;
解码文件(文件路径,o);
//我们要扩展到的新尺寸
所需的最终int_SIZE=1024;
//找到正确的刻度值。它应该是2的幂。
内部宽度=o.向外宽度,高度=o.向外高度;
int标度=1;
while(true){
如果(宽度\u tmp<要求的\u尺寸和高度\u tmp<要求的\u尺寸)
打破
宽度_tmp/=2;
高度_tmp/=2;
比例*=2;
}
//用inSampleSize解码
BitmapFactory.Options o2=新的BitmapFactory.Options();
o2.inSampleSize=刻度;
位图=BitmapFactory.decodeFile(文件路径,o2);
返回位图;
}
愿它有帮助

如果您希望高效加载大型位图,请遵循使用或库:

用本地文件夹路径或服务器url替换路径

Glide.with (context).load (path).asBitmap().into(imageView);
使用或库:

用本地文件夹路径或服务器url替换路径

Glide.with (context).load (path).asBitmap().into(imageView);

我读了所有的答案,这就是我打算做的,告诉我它是否有效。在我的
应用程序
类中,在
onCreate()
中,我将设备宽度和高度以像素为单位存储在
共享参考
中。在修改后的代码中,我根据设备像素缩放位图。希望这能避免我犯错误

private static Bitmap getBitmapFromUri(@NonNull Context context, @NonNull Uri uri) throws IOException {

        ParcelFileDescriptor parcelFileDescriptor =
                context.getContentResolver().openFileDescriptor(uri, "r");
        assert parcelFileDescriptor != null;
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        parcelFileDescriptor.close();

        TinyDB tinyDB = new TinyDB(context);
        int maxSize = Math.min(tinyDB.getInt(AppConstants.DEVICE_WIDTH, 720), tinyDB.getInt(AppConstants.DEVICE_HEIGHT, 1080));
        int outWidth;
        int outHeight;
        int inWidth = image.getWidth();
        int inHeight = image.getHeight();
        if(inWidth > inHeight){
            outWidth = maxSize;
            outHeight = (inHeight * maxSize) / inWidth;
        } else {
            outHeight = maxSize;
            outWidth = (inWidth * maxSize) / inHeight;
        }

        return Bitmap.createScaledBitmap(image, outWidth, outHeight, false);
    }

我读了所有的答案,这就是我打算做的,告诉我它是否有效。在我的
应用程序
类中,在
onCreate()
中,我将设备宽度和高度以像素为单位存储在
共享参考
中。在修改后的代码中,我根据设备像素缩放位图。希望这能避免我犯错误

private static Bitmap getBitmapFromUri(@NonNull Context context, @NonNull Uri uri) throws IOException {

        ParcelFileDescriptor parcelFileDescriptor =
                context.getContentResolver().openFileDescriptor(uri, "r");
        assert parcelFileDescriptor != null;
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        parcelFileDescriptor.close();

        TinyDB tinyDB = new TinyDB(context);
        int maxSize = Math.min(tinyDB.getInt(AppConstants.DEVICE_WIDTH, 720), tinyDB.getInt(AppConstants.DEVICE_HEIGHT, 1080));
        int outWidth;
        int outHeight;
        int inWidth = image.getWidth();
        int inHeight = image.getHeight();
        if(inWidth > inHeight){
            outWidth = maxSize;
            outHeight = (inHeight * maxSize) / inWidth;
        } else {
            outHeight = maxSize;
            outWidth = (inWidth * maxSize) / inHeight;
        }

        return Bitmap.createScaledBitmap(image, outWidth, outHeight, false);
    }


同时发布decodeFileDescriptor()方法。向下采样图像。签出这个链接@saurabhgupta我还没有写这个函数。它是内置的。我在Android文档中发现了这种方法。如果位图会出现OOM错误,我只需要修改它以获得最佳的位图缩小版本。@AmitTiwari…请参阅我的回答,同时发布decodeFileDescriptor()方法。向下采样您的图像。签出这个链接@saurabhgupta我还没有写这个函数。它是内置的。我在Android文档中发现了这种方法。如果位图将出现OOM错误,我只需要修改它以获得最佳的位图缩小版本。@Amittwari…请参阅我的回答我不想硬编码任何值,如100。我希望它是最好的质量,这样它就不会溢出内存。我该怎么做呢?这张图片是来自URL吗?如果是的话,不要创建位图,只要用毕加索来显示它就不会重新调整大小了,我知道。但图像是从手机的多媒体资料中加载的。我不想硬编码任何值,比如100。我希望它是最好的质量,这样它就不会溢出内存。我该怎么做呢?这张图片是来自URL吗?如果是的话,不要创建位图,只要用毕加索来显示它就不会重新调整大小了,我知道。但图像正在从手机的多媒体资料中加载。您的解决方案很好。但我只是不想硬编码所需的大小。我希望它是动态确定的。有办法吗?因为所需的_大小可能不会在高端手机上提供OOM,但在低端手机上可能会提供OOM。您的解决方案很好。但我只是不想硬编码所需的大小。我希望它是动态确定的。有办法吗?因为所需的大小可能不会在高端手机上提供OOM,但在低端手机上可能会提供OOM。如何确定图像的最大大小?通常它与图像视图的高度相对应