Glide 4.11使用时出现“trying to use a recycled bitmap”崩溃,转换操作可能会导致这种情况吗?

3

我知道已经有很多与“尝试使用已回收的位图”崩溃相关的问题,但是没有一个能帮助我。

细节如下:

  • 此项目中任何地方都没有调用Bitmap.recycle()。
  • 所有图像都使用Glide(4.11.0)加载。
  • Glide调用都很简单,没有使用占位符。
  • 崩溃似乎在切换片段时随机发生。

我能想到的唯一可能就是转换。这个项目中只有两个转换:

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)
}

很抱歉在这里粘贴了那么多代码(希望对某些人有用)。 这些转换器或加载器会导致崩溃吗?

1个回答

1
为了给你的图片赋予圆形、正方形或椭圆形的形状,您不需要转换图片。 MaterialDesign 已经推出了 ShapeableImageView,可以让您在运行时为图片添加形状,并且您还可以添加带颜色的边框。
  1. add matrial dependecies :

    implementation 'com.google.android.material:material:1.3.0-alpha01'
    
  2. Add shapeableImageView in your xyz.xml:

    <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"
        />
    
  3. Add style inside res/values/style.xml file

    <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加载您的图像。

谢谢,了解到ShapeableImageView的信息很好,但我仍然需要ClipWhiteTransformation,因为我不知道从服务器传来的图像上有多少白色边框。此外,这些图像需要保持变换,并使用共享元素转换,不确定ShapeableImageView将如何处理它。 - Bob Steve

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