在Android应用程序中加载位图是一项棘手的任务。位图占用大量内存。例如,在Pixel 6 Pro上拍摄的照片具有大约1250万像素(4080 x 3072)。使用ARGB_的位图配置(每像素4个字节),将照片加载到内存中需要大约50MB的内存。如此大的位图会很快耗尽应用程序的内存预算。
幸运的是,有很多图书馆帮助我们解决这个问题。一些流行的图像加载库包括来自Square的毕加索、Instacart的Coil、Facebook的Fresco和Typeguard,Inc.的Glide。这些库简化了大部分与位图和Android上其他类型图像相关的复杂任务,例如获取、解码和显示位图。很高兴了解这些库如何有效地处理位图,使我们的生活更轻松。在本文中,我们将讨论处理位图时可能遇到的一些问题以及如何解决这些问题。
位图的问题
我们已经讨论过位图会占用大量的应用程序内存。这可能会导致几个问题:
可能 发生内存不足错误
UI或ANR响应速度慢
图像的缓慢获取和显示
让我们看看如何解决这些问题。
内存不足
图像有各种形状和大小。在大多数情况下,它们比显示它们所需的UI组件大。将4080 x 3072图像加载到内存中,然后仅在较小的1020 x 768视图上显示它是没有意义的。高分辨率的图像不会带来任何可见的好处,但仍会占用宝贵的内存。理想情况下,我们只想将图像的较小版本加载到内存中。我们怎么能做到呢?
首先,我们需要找出图像有多大 位图工厂类提供一个名为InJustDecodeBounds,它可用于读取图像的尺寸和类型,而无需为该位图实际分配内存。
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeResource(resources, R.id.myimage, options)
val imageHeight: Int = options.outHeight
val imageWidth: Int = options.outWidth
val imageType: String = options.outMimeType
在找到图像维度之后,我们需要加载它的缩小版本,以匹配要加载图像的目标UI组件的尺寸。我们可以使用 无样品选项。请注意,解码器使用基于2的幂的最终值,任何其他值都将向下舍入到最接近的2的幂次。
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// Raw height and width of image
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
UI响应速度慢
在UI线程上加载位图会降低应用程序的性能。因此,在后台线程上解码位图是很重要的。此外,我们应该在内存上建立一个缓存来快速访问解码后的位图。
在Android中,生成位图缓存的推荐方法是使用LruCache,在过去,流行的内存缓存实现是SoftReference或 WeakReference公司位图缓存。然而,在后一个Android版本中,垃圾收集器在收集软/弱引用方面更为积极,这使得它们相当无效。做好LruCache,我们应该考虑一些因素,例如:
其余活动和应用程序的内存密集程度如何,缓存中的每个位图将占用多少内存?
质量与数量的权衡。存储大量低质量位图是否更好?
一次屏幕上会显示多少图像?访问这些图像的频率是多少?
回答这些问题将帮助我们确定缓存的合适大小。
除此之外,位图很大,因此它使垃圾回收器(GC)更频繁地运行。在这里,您可以使用位图池来提高处理位图的效率。位图池将重用位图,避免在应用程序中连续分配和取消分配内存,减少GC开销。GC运行时间越短,应用程序完成任务的时间就越长。
下载速度慢的图像
通过网络发送数据可能需要很长时间,尤其是对于位图这样的大数据。磁盘缓存通常可以帮助组件快速重新加载下载的图像。当然,从磁盘获取图像比从内存加载要慢,但它仍然非常有用,因为内存缓存并不总是可用的。内存缓存可以用大型数据集快速填充,如果应用程序在后台太长时间,则会被销毁。
在图像访问频率更高的应用程序中(如图像库应用程序),ContentProvider可能是存储缓存图像的更合适的位置。
网友评论