如何从Uri获取Bitmap?

268

如何从Uri获取Bitmap对象(如果我成功将其存储在/data/data/MYFOLDER/myimage.pngfile///data/data/MYFOLDER/myimage.png中),以便在我的应用程序中使用?

有没有人有实现这个的想法?

24个回答

645

这是正确的做法:

protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK)
    {
        Uri imageUri = data.getData();
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
    }
}

如果您需要加载非常大的图像,则可以使用以下代码将其分块加载(避免大内存分配):

BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(myStream, false);  
Bitmap region = decoder.decodeRegion(new Rect(10, 10, 50, 50), null);

请在这里查看答案here


3
这段代码无法处理更大的图像(基本上是任何壁纸大小的图像)。getBitmap() 调用 decodeStream(),该方法在处理 https://dev59.com/m3E95IYBdhLWcg3wp_gg 中描述的OOM错误时会失败。还有其他建议吗?MediaStore.Images.Thumbnails.getThumbnail() 显然不接受 contentURI。 - pjv
1
在这里查看答案: https://dev59.com/0FPTa4cB1Zd3GeqPkZTp - Mark Ingram
@MarkIngram 这个能用于任何本地图片还是只能用于相机拍摄的图片? - Narendra Singh
它告诉我Unhandled exception: java.io.IOException并在下面显示红色波浪线。你有什么想法为什么会这样? - Cullub
20
MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri); 现在已经过时了。 - Rohit Singh
显示剩余10条评论

124

以下是正确的方式,同时注意内存使用情况:

protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
  super.onActivityResult(requestCode, resultCode, data);
  if (resultCode == RESULT_OK)
  {
    Uri imageUri = data.getData();
    Bitmap bitmap = getThumbnail(imageUri);
  }
}

public static Bitmap getThumbnail(Uri uri) throws FileNotFoundException, IOException{
  InputStream input = this.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();

  if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
    return null;
  }

  int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;

  double ratio = (originalSize > THUMBNAIL_SIZE) ? (originalSize / THUMBNAIL_SIZE) : 1.0;

  BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
  bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
  bitmapOptions.inDither = true; //optional
  bitmapOptions.inPreferredConfig=Bitmap.Config.ARGB_8888;//
  input = this.getContentResolver().openInputStream(uri);
  Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
  input.close();
  return bitmap;
}

private static int getPowerOfTwoForSampleRatio(double ratio){
  int k = Integer.highestOneBit((int)Math.floor(ratio));
  if(k==0) return 1;
  else return k;
}

根据Mark Ingram的帖子,getBitmap()调用也会调用decodeStream(),因此您不会失去任何功能。

参考资料:


1
这真的帮了我很多,虽然值得一提的是,在静态上下文中无法使用this关键字。我将它作为参数传递到getThumbnail方法中,它像魔法一样工作。 - MacKinley Smith
9
请问应该给THUMBNAILSIZE赋予什么数值? - Abid
2
关闭和重新打开InputStream实际上是必要的,因为第一个BitmapFactory.decodeStream(...)调用将流的读取位置设置为末尾,所以在不重新打开流的情况下,方法的第二次调用将无法工作! - DominicM
THUMBNAILSIZE,正如其名称所示,是要显示为缩略图的图像的大小,即缩略图显示大小。 - Abx
3
不必亲自将比率计算为二的幂,因为解码器会将样本大小舍入为最接近二的幂。因此可以跳过方法调用“getPowerOfTwoForSampleRatio()”。请参见:https://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize - winklerrr
显示剩余2条评论

60

看起来 MediaStore.Images.Media.getBitmapAPI 29 中已被弃用。建议使用在API 28中添加的ImageDecoder.createSource 来代替。

以下是获取位图的方法:

val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    ImageDecoder.decodeBitmap(ImageDecoder.createSource(requireContext().contentResolver, imageUri))
} else {
    MediaStore.Images.Media.getBitmap(requireContext().contentResolver, imageUri)
}

重要提示: ImageDecoder.decodeBitmap 读取EXIF方向信息,Media.getBitmap则不会。


2
文档明确指出:“此方法应在工作线程中执行,而不是主线程中执行!” - Xenolion
这在较新版本的安卓系统上无法运行。 - dessalines

51
try
{
    Bitmap bitmap = MediaStore.Images.Media.getBitmap(c.getContentResolver() , Uri.parse(paths));
}
catch (Exception e) 
{
    //handle exception
}

是的,路径必须符合以下格式:

file:///mnt/sdcard/filename.jpg


2
@Dhananjay 谢谢你,你的提示救了我的一天,并且它可以从内容提供者中加载缩略图位图。 - Nezneika
2
此外,Uri.parse() 必须包含 URI 格式,就像这样:_Uri.parse("file:///mnt/sdcard/filename.jpg")_,否则我们将会得到 java.io.FileNotFoundException: No content provider - Nezneika
这是一个好的简明回答,适用于大多数情况。虽然一些编辑意见会更好,但这是一个很好的答案,为了提炼出其他答案中直接回答OP问题的部分,将其放在页面上是不错的选择。 - umassthrower
2
@AndroidNewBee c 是上下文对象。 - DjP
在我的情况下无法工作,出现异常Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getScheme()' on a null object reference。 - K Guru

26
private void uriToBitmap(Uri selectedFileUri) {
    try {
        ParcelFileDescriptor parcelFileDescriptor =
                getContentResolver().openFileDescriptor(selectedFileUri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);

        parcelFileDescriptor.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4
它适用于所有的SDK...谢谢。 它是Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);的替代方法。 - Jigar Patel
1
MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri) 已经被弃用。 - oiyio

25

这是最简单的解决方案:

Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);

太棒了,它正常运行。 - Chauhan Ajay

12
您可以像这样从URI中检索位图。
Bitmap bitmap = null;
try {
    bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
} catch (IOException e) {
    e.printStackTrace();
}

12
Uri imgUri = data.getData();
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imgUri);

2
请您详细说明一下这段代码的工作原理以及它如何回答这个问题? - Michael Dodd

6
Bitmap bitmap = null;
ContentResolver contentResolver = getContentResolver(); 
try {
    if(Build.VERSION.SDK_INT < 28) {
        bitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri);
    } else {
        ImageDecoder.Source source = ImageDecoder.createSource(contentResolver, imageUri);
        bitmap = ImageDecoder.decodeBitmap(source);
    }
} catch (Exception e) {
    e.printStackTrace();
}

5
private fun setImage(view: ImageView, uri: Uri) {
        val stream = contentResolver.openInputStream(uri)
        val bitmap = BitmapFactory.decodeStream(stream)
        view.setImageBitmap(bitmap)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接