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

Android Bad notification posted崩溃 自定义通知Bitmap转PNG方案求助

问题根本原因分析

你的崩溃和Bitmap是否为PNG格式无关,错误原因主要是两点:

  • 并发访问冲突:你的updateDataUsage方法是从工作线程每秒调用一次,全局复用同一个Bitmap、Canvas、Notification.Builder实例,会出现上一次通知还在被系统渲染处理,下一次调用已经修改了共享的Bitmap和Builder属性,触发并发修改异常。
  • 数组越界风险:你直接读取BytesConversion.convertSpeed返回的数组下标0和1,没有做长度校验,一旦方法返回数组长度不足2就会触发越界崩溃。
修复方案

1. 移除全局共享的可变资源

不要全局存储Bitmap、Canvas、Notification.Builder实例,每次更新通知时局部创建对应资源,避免多线程冲突。

2. 增加数组长度校验

读取convertSpeed返回结果前先校验数组长度,避免越界。

3. (可选)Bitmap转PNG格式的实现

如果需要显式转PNG格式,可以参考如下工具方法:

fun bitmapToPngIcon(context: Context, bitmap: Bitmap): Icon {
    val outputStream = ByteArrayOutputStream()
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
    val pngBytes = outputStream.toByteArray()
    return Icon.createWithData(pngBytes, 0, pngBytes.size)
}
核心方法修改示例
// 移除全局的bitmap、canvas、mNotificationBuilder、tempVariables变量
// 把paint改成全局只读,避免重复创建
private val paintSpeed = Paint().apply {
    color = Color.WHITE
    isAntiAlias = true
    textSize = 60f
    textAlign = Paint.Align.CENTER
    typeface = Typeface.DEFAULT_BOLD
}
private val paintUnit = Paint().apply {
    color = Color.WHITE
    isAntiAlias = true
    textSize = 40f
    textAlign = Paint.Align.CENTER
    typeface = Typeface.DEFAULT_BOLD
}
// 把PendingIntent提前初始化好全局复用
private val pendingIntentNotification = PendingIntent.getActivity(
    context,
    notificationPersistentChannelID,
    intentNotification,
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

fun updateDataUsage(dataSpeed: Long, remainingData: String?, planDetails: String?) {
    // 每次调用局部创建Bitmap和Canvas
    val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)

    val tempValues = BytesConversion.convertSpeed(dataSpeed)
    // 先校验数组长度
    val speedText = if (tempValues.size >= 2) tempValues[0] else "0"
    val unitText = if (tempValues.size >= 2) tempValues[1] else "Kb/s"
    canvas.drawText(speedText, 50f, 50f, paintSpeed)
    canvas.drawText(unitText, 50f, 95f, paintUnit)

    try {
        // 每次重新构建Builder,不要复用全局实例
        val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            getNotificationBuilder()
        } else {
            // 兼容低版本的Builder创建逻辑
            NotificationCompat.Builder(context, notificationChannelPrimary)
                .setContentIntent(pendingIntentNotification)
                .setAutoCancel(false)
                .setStyle(NotificationCompat.BigTextStyle())
                .setOnlyAlertOnce(true)
                .setColor(ContextCompat.getColor(context, R.color.primary_color))
        }
        builder.setContentTitle(remainingData)
        builder.setContentText(planDetails)
        // 可以直接用createWithBitmap,也可以用上面的bitmapToPngIcon转成PNG格式传入
        builder.setSmallIcon(Icon.createWithBitmap(bitmap))
        notificationManager.notify(notificationPersistentChannelID, builder.build())
        // 用完回收Bitmap减少内存占用
        bitmap.recycle()
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

同理修改setZeroSpeed方法,和上面逻辑一致,每次局部创建资源,不复用全局实例即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 11:24:03