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
相关产品推荐
相关产品推荐

