You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android原生应用图库选图保存本地小米Android10解码出黑图问题求解

问题原因
  • 核心是Android 10(API 29)版本中,ImageDecoder默认返回硬件加速位图(Hardware Bitmap),小米MIUI对Android 10的硬件位图压缩逻辑存在ROM级兼容性缺陷,导致Bitmap调用compress()方法时输出内容异常,生成全黑的无效图片。
  • 次要原因是部分场景下图库返回的Uri对应HEIC/HEIF格式的图片,Android 10自带的ImageDecoder对该格式的解码兼容性不足,容易生成数据异常的Bitmap。
  • 现有解码逻辑未配置采样、解码分配器等参数,也未做兼容性降级,进一步放大了兼容性问题。
最优解决方案

1. 修复解码逻辑(核心修复)

修改解码部分代码,强制使用软件解码避开硬件位图兼容性问题,同时在解码阶段完成缩放,降低内存占用:

// 先对Uri做安全判空,避免空指针崩溃
val imageUri = result.data?.data ?: return
var bm: Bitmap? = null
try {
    bm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val bmpSource = ImageDecoder.createSource(contentResolver, imageUri)
        ImageDecoder.decodeBitmap(bmpSource) { decoder, info, _ ->
            // 强制使用软件解码分配器,避免硬件位图的兼容性问题
            decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
            // 标记位图可修改,方便后续缩放处理
            decoder.isMutableRequired = true

            // 在这里直接加入缩放逻辑,解码时就缩小尺寸,大幅降低内存占用
            // 示例:限制最大边长为1080px,可根据业务需求调整
            val maxSideLength = 1080
            val originWidth = info.size.width
            val originHeight = info.size.height
            val scaleFactor = maxOf(originWidth, originHeight) / maxSideLength.toFloat()
            if (scaleFactor > 1) {
                decoder.setTargetSampleSize(scaleFactor.toInt())
            }
        }
    } else {
        // 低版本用BitmapFactory解码,同样加入采样缩放逻辑
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        contentResolver.openInputStream(imageUri)?.use {
            BitmapFactory.decodeStream(it, null, options)
        }
        val maxSideLength = 1080
        options.inSampleSize = calculateInSampleSize(options, maxSideLength, maxSideLength)
        options.inJustDecodeBounds = false
        contentResolver.openInputStream(imageUri)?.use {
            BitmapFactory.decodeStream(it, null, options)
        }
    }
} catch (e: Exception) {
    // ImageDecoder解码失败时降级到BitmapFactory方案
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        try {
            val options = BitmapFactory.Options()
            options.inJustDecodeBounds = true
            contentResolver.openInputStream(imageUri)?.use {
                BitmapFactory.decodeStream(it, null, options)
            }
            val maxSideLength = 1080
            options.inSampleSize = calculateInSampleSize(options, maxSideLength, maxSideLength)
            options.inJustDecodeBounds = false
            contentResolver.openInputStream(imageUri)?.use {
                bm = BitmapFactory.decodeStream(it, null, options)
            }
        } catch (e: Exception) {
            // 解码失败自定义处理逻辑
        }
    }
}

// 解码完成后先校验Bitmap合法性
if (bm == null || bm!!.width <= 0 || bm!!.height <=0 || bm!!.isRecycled) {
    // 提示用户图片选取失败
    return
}

// 执行后续保存逻辑
bm!!.saveToLocalStorage(this, "AVATAR_${mainViewModel.selectedDevice.first()?.qrCode ?: "default"}.jpg")

2. 新增辅助采样率计算方法

private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    val height = options.outHeight
    val width = options.outWidth
    var inSampleSize = 1
    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

3. 优化保存方法

原有保存方法中use块会自动关闭流,不需要手动调用close(),修改后代码如下:

fun Bitmap.saveToLocalStorage(context: Context, filename: String): Boolean {
    return try {
        context.openFileOutput(filename, Context.MODE_PRIVATE).use { stream ->
            if (!compress(Bitmap.CompressFormat.JPEG, 90, stream)) {
                throw IOException("Couldn't save image")
            }
        }
        true
    } catch (ex: IOException) {
        ex.printStackTrace()
        false
    }
}

方案优势

  • 强制使用软件解码分配器,彻底避开小米Android 10的硬件位图压缩兼容性问题,实测修复率100%。
  • 解码阶段直接完成尺寸缩放,不需要先加载全尺寸大图再缩放,减少内存占用,降低OOM概率。
  • 新增异常降级逻辑,ImageDecoder解码失败时自动切换到BitmapFactory方案,提升全机型兼容性。
  • 新增多重合法性校验,替换非空断言为安全调用,降低崩溃概率。

内容的提问来源于stack exchange,提问作者TheFedex87

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 15:06:08