如何从Uri获取位图?
如何从Uri获取位图?
在Android的API 29中,MediaStore.Images.Media.getBitmap
方法被废弃了。推荐的方法是使用ImageDecoder.createSource
方法,该方法在API 28中添加。
下面是获取位图的方法:
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
方法则不行。
文档明确指出:“此方法应在工作线程中执行,而不是主线程中!”
然而,这种方法在较新的Android版本上无法正常工作。
问题的原因是在API 29中,Android废弃了MediaStore.Images.Media.getBitmap
方法。为了替代这个方法,推荐使用ImageDecoder.createSource
方法来获取位图。然而,仅仅替换方法是不够的,还需要注意ImageDecoder.decodeBitmap
方法可以读取EXIF方向信息,而Media.getBitmap
方法则不行。此外,根据文档的建议,获取位图的操作应该在工作线程中执行,而不是主线程中。
解决方法是根据Android版本的不同,使用不同的方法来获取位图。在API 28及更高版本中,可以使用ImageDecoder.createSource
方法和ImageDecoder.decodeBitmap
方法来获取位图。而在API 28以下的版本中,可以继续使用MediaStore.Images.Media.getBitmap
方法。
需要注意的是,使用ImageDecoder.createSource
方法和ImageDecoder.decodeBitmap
方法获取位图时,需要在工作线程中执行,而不是主线程中。这样可以避免阻塞主线程,提高应用的性能和响应速度。
如何从Uri获取位图?
在上述代码中,我们可以看到这个问题的出现主要是因为需要从Uri获取位图。解决方法是使用getContentResolver().openInputStream(uri)方法打开Uri对应的输入流,然后使用BitmapFactory.decodeStream(input, null, options)方法将输入流解码为位图。在解码过程中,我们可以设置一些选项,如inJustDecodeBounds、inSampleSize等,以控制内存的使用和位图的大小。
在这段代码中,我们可以看到以下解决方法的步骤:
1. 在onActivityResult方法中,获取返回的Uri对象,并调用getThumbnail方法获取位图。
2. 在getThumbnail方法中,首先使用getContentResolver().openInputStream(uri)方法打开Uri对应的输入流。
3. 创建一个BitmapFactory.Options对象,并将其inJustDecodeBounds属性设置为true,以获取位图的原始尺寸。
4. 调用BitmapFactory.decodeStream(input, null, onlyBoundsOptions)方法,将输入流解码为位图,但不将其加载到内存中。
5. 关闭输入流。
6. 判断位图的宽度和高度是否为-1,如果是,则返回null。
7. 计算原始尺寸和缩略图大小之间的比例,并根据比例计算出inSampleSize值。
8. 创建一个新的BitmapFactory.Options对象,并将其inSampleSize属性设置为计算得到的值。
9. 再次打开输入流,并调用BitmapFactory.decodeStream(input, null, bitmapOptions)方法,将输入流解码为缩略图位图。
10. 关闭输入流,并返回缩略图位图。
此外,还有一些其他的注意事项和建议:
- 在静态方法中,无法直接使用this关键字,可以通过将其作为参数传递给方法来解决这个问题。
- THUMBNAIL_SIZE是缩略图的大小,可以根据需要进行调整。
- 需要关闭和重新打开输入流的原因是,第一次调用BitmapFactory.decodeStream(...)方法会将输入流的读取位置设置为末尾,所以第二次调用该方法时,如果不重新打开输入流,将无法正常工作。
- THUMBNAIL_SIZE应该是要显示缩略图的视图的大小。
- 不需要自己计算比例的两个幂,因为解码器本身会将采样大小舍入到最接近的两个幂。因此,可以跳过getPowerOfTwoForSampleRatio()方法的调用。
通过使用getContentResolver().openInputStream(uri)方法打开Uri对应的输入流,并使用BitmapFactory.decodeStream(input, null, options)方法将输入流解码为位图,我们可以从Uri获取位图。在解码过程中,可以使用一些选项来控制内存的使用和位图的大小。
如何从Uri获取Bitmap?
问题的出现原因:
- 问题最初是由于大图像加载而引起的内存溢出错误。getBitmap()方法调用decodeStream(),而后者在处理大图像时会导致OOM错误。
- 另一个问题是,如果只知道图像的路径而无法访问data.getData()方法,该如何获取Uri和Bitmap对象。
解决方法:
- 对于大图像加载,可以使用以下代码以瓦片的方式加载图像,避免大内存分配:
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(myStream, false); Bitmap region = decoder.decodeRegion(new Rect(10, 10, 50, 50), null);
- 对于无法访问data.getData()方法的情况,可以参考这里的文档,并提出新的问题以获取解决方案。
- 代码中的try-catch块不是必需的,但在某些情况下,getBitmap()方法可能会抛出异常。
需要注意的是,MediaStore.Images.Media.getBitmap()方法已经被弃用,可以使用其他方法来获取Bitmap对象。
从Uri获取Bitmap的过程中,需要注意内存溢出错误和无法访问data.getData()方法的问题。在处理大图像时,可以使用瓦片加载的方法来避免内存溢出。如果无法访问data.getData()方法,可以通过提出新的问题来获取解决方案。另外,需要注意某些方法的弃用情况,可以使用其他方法来获取Bitmap对象。