android - 使用 Glide 4.11 “尝试使用回收的位图”时崩溃,转换会导致这种情况吗?
问题描述
我知道已经有很多与“尝试使用回收的位图”崩溃相关的问题,但没有一个对我有帮助。
细节:
- 在这个项目的任何地方都没有调用 Bitmap.recycle()
- 所有图像均使用 Glide (4.11.0) 加载
- Glide 调用都很简单,而且不使用占位符
- 切换片段时崩溃似乎是随机发生的
我唯一能想到的就是转变。此项目中只有 2 个转换。
CircleTransformation(将图像剪辑为具有自定义半径的圆):
class CircleTransformation(private val radius: Float) : BitmapTransformation() {
companion object {
private const val ID = "com.project.transformation.circle"
private val ID_BYTES: ByteArray = ID.toByteArray()
}
public override fun transform(pool: BitmapPool, source: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
val paint = Paint()
paint.isAntiAlias = true
paint.shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val halfWidth = source.width / 2f
val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
canvas.drawCircle(
halfWidth,
(source.height / 2).toFloat(),
halfWidth * radius,
paint
)
return output
}
// Caching helpers
override fun equals(other: Any?): Boolean {
return other is CircleTransformation && other.hashCode() == hashCode()
}
override fun hashCode(): Int {
return ID.hashCode()
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(ID_BYTES)
}
}
和 ClipWhiteTransformation (从图像中删除白色边框):
class ClipWhiteTransformation() : BitmapTransformation() {
companion object {
private const val ID = "com.project.transformation.clipWhite"
private val ID_BYTES: ByteArray = ID.toByteArray()
// Config
const val white = 253 // White pixel, if all channels are equal or greater than this
const val transparent = 50 // Transparent pixel, if Less than this
}
public override fun transform(pool: BitmapPool, source: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
val width = source.width - 1
val height = source.height - 1
val halfX = width / 2
val halfY = height / 2
var startY = 0
// Left Margin
var left = 0
for (x in 0 until halfX) {
val pixel = source.getPixel(x, halfY)
// Transparent?
if (Color.alpha(pixel) < transparent) continue
// Not white?
if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
left = x
if (x > 2) {
startY = 2
}
break
}
}
// Right Margin
var right = 0
for (x in 0 until halfX) {
val pixel = source.getPixel(width - x, halfY)
// Transparent?
if (Color.alpha(pixel) < transparent) continue
// Not white?
if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
right = x
if (x > 2) {
startY = 2
}
break
}
}
// Top Margin
var top = 0
for (y in startY until halfY) {
val pixel = source.getPixel(halfX, y)
// Transparent?
if (Color.alpha(pixel) < transparent) continue
// Not white?
if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
top = y
break
}
}
// Bottom Margin
var bottom = 0
for (y in startY until halfY) {
val pixel = source.getPixel(halfX, height - y)
// Transparent?
if (Color.alpha(pixel) < transparent) continue
// Not white?
if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
bottom = y
break
}
}
// Clip, scale and return
val newWidth = width - (left + right)
val newHeight = height - (top + bottom)
val scale = if (abs(newWidth - outWidth) > abs(newHeight - outHeight)) outWidth / newWidth.toFloat() else outHeight / newHeight.toFloat()
val matrix = Matrix().apply { setScale(scale, scale) }
return Bitmap.createBitmap(source, left, top, newWidth, newHeight, matrix, false)
}
// Caching helpers
override fun equals(other: Any?): Boolean {
return other is ClipWhiteTransformation && other.hashCode() == hashCode()
}
override fun hashCode(): Int {
return ID.hashCode()
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(ID_BYTES)
}
}
最初使用的是 BitmapPool,删除它并没有阻止崩溃。
顺便说一句,这是用于加载图像的扩展:
fun ImageView.setURL(url: String,
@DrawableRes error: Int? = null,
@DrawableRes placeholder: Int? = null,
size: Int? = null,
options: ((RequestBuilder<Drawable>) -> Unit)? = null,
completion: ((resource: Drawable?) -> Unit)? = null) {
// No URL, use Placeholder if exists, if not, use the error image
if (url.isEmpty()) {
placeholder?.also{ setImageResource(it) } ?: run { error?.also{ setImageResource(it) } }
return
}
Glide.with(applicationInstance) // (I'm using an application instance directly here)
.load(url).apply {
completion?.also { completion ->
this.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
completion(null)
return false
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
completion(resource)
return false
}
})
}
}
.apply { size?.also { this.override(it)} }
.apply { options?.invoke(this) }
.placeholder(placeholder ?: 0)
.error(error ?: 0)
.transition(DrawableTransitionOptions.withCrossFade(350))
.into(this)
}
很抱歉在这里粘贴了这么多代码(希望对某人有用)。这些转换或加载程序会导致崩溃吗?
解决方案
塑造(circle/square/oval)
你的形象 你不需要改变你的形象。
MaterialDesign
已经介绍了ShapeableImageView
它可以让你在运行时塑造你的图像,你也可以用颜色添加边框。
添加物质依赖:
implementation 'com.google.android.material:material:1.3.0-alpha01'
在 xyz.xml 中添加 shapeableImageView:
<com.google.android.material.imageview.ShapeableImageView android:id="@+id/imgStudent" android:layout_width="100dp" android:layout_height="100dp" app:shapeAppearanceOverlay="@style/circleImageView" android:padding="2dp" app:strokeColor="@color/white" app:strokeWidth="5dp" android:scaleType="centerCrop" android:adjustViewBounds="true" tools:srcCompat="@drawable/ic_kid_placeholder" />
在 res/values/style.xml 文件中添加样式
<style name="circleImageView" parent=""> <item name="cornerFamily">rounded</item> <item name="cornerSize">50%</item> <item name="android:shadowRadius">100</item> <item name="android:shadowColor">@color/gray</item> <item name="backgroundOverlayColorAlpha">12</item> </style>
最后用 glide 加载你的图像。
推荐阅读
- swift - 有没有办法只为一个集合启用离线持久性,而不是其他集合?
- mongodb - 如何使用 mongoose 从 mongodb 中的数组中删除对象
- r - 从列中提取数据以创建新的数据框
- python - 根据列表检查多个用户输入
- javascript - 在下面列出的代码上使用反应拆分技术的最佳方法是什么
- python - 合并两个数据框但忽略带有 NaT 的行
- visual-studio-code - 函数名称中的 VS Code 高亮选择
- javascript - 播放来自其他来源的音频源时,Smart TV Internet Browser 会停止播放视频。有没有办法在没有视频“黑屏”的情况下播放音频?
- python - PyTorch 数据集:并行读取多个音频文件错误 Can't get attribute 'AudioDataset' on
我正在尝试创建一个 PyTorch
Dataset
来读取两个音频文件作为功能。该代码旨在采用两条音频路径并对其进行预处理并返回频谱图、特征等,这需要大量时间。在创建数据集时,虽然数据集很小,但 900 个文件需要花费 15 分钟左右的大量- google-apps-script - Google Scripts:执行基本操作的自定义函数中的#ERROR