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

Foreground Service中Notification Canvas绘制更新停止问题排查

问题分析与解决方案

你的问题是前台服务的倒计时通知运行约两分钟后停止更新,但onTick的日志还在输出,说明倒计时逻辑正常,问题出在通知更新的环节。以下是代码中的关键问题和修复方案:

1. 重复创建未回收的Bitmap导致内存压力

每次onTick都会调用createBitmapFromText生成新的Bitmap,但旧的Bitmap没有被回收,随着时间推移内存占用持续升高,系统可能会限制通知的更新操作,甚至导致Bitmap创建失败。

修复方案:

  • 在设置新Bitmap前,回收旧的Bitmap资源;
  • 尽量复用Bitmap的尺寸(如果倒计时文本宽度变化不大),避免每次创建新的Bitmap对象。

2. 未重新构建Notification实例

你修改了contentView的内容,但一直复用最初创建的notification对象。系统会缓存Notification实例,直接调用notify可能不会触发UI更新,因为对象引用没有变化。

修复方案:
每次更新contentView后,需要重新构建Notification(或者使用NotificationCompat.Builder重新build),再调用notify。

3. 重复加载Typeface浪费资源

createBitmapFromText中每次都从assets加载Typeface,这是IO操作,重复执行会耗时且浪费资源,可能导致主线程阻塞。

修复方案:
提前缓存Typeface实例,避免重复加载。


修复后的代码示例

缓存Typeface并优化Bitmap创建

// 类级别缓存Typeface,避免重复加载
private val cachedTypeface by lazy {
    Typeface.createFromAsset(
        context.assets,
        "cairo_medium_500.ttf"
    )
}

fun createBitmapFromText(context: Context, messageText: String, textSize: Float, textColor: Int): Bitmap {
    val paint = Paint().apply {
        this.textSize = textSize
        typeface = cachedTypeface
        color = textColor
        textAlign = Paint.Align.LEFT
        isAntiAlias = true // 提升文本显示清晰度
    }

    val baseline = -paint.ascent()
    val width = (paint.measureText(messageText) + 0.5f).toInt()
    val height = (baseline + paint.descent() + 0.5f).toInt()
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    canvas.drawText(messageText, 0f, baseline, paint)
    return bitmap
}

修复通知更新逻辑

// 假设你持有全局的NotificationCompat.Builder实例
private lateinit var notificationBuilder: NotificationCompat.Builder

private fun startLiveNextPrayerTimer(futureMillisTime: Long, context: Context) {
    countDownTimer = object : CountDownTimer(futureMillisTime, 1000) {
        override fun onTick(millisUntilFinished: Long) {
            val timeText = secondsToHHmmss(millisUntilFinished / 1000)
            Log.e("onTick", timeText)

            // 回收旧Bitmap释放内存
            val oldBitmap = contentView.getImageViewBitmap(R.id.tvCounter)
            oldBitmap?.recycle()

            // 生成新Bitmap并更新RemoteViews
            val newBitmap = createBitmapFromText(context, timeText, 40.0f, Color.GRAY)
            contentView.setImageViewBitmap(R.id.tvCounter, newBitmap)

            // 重新构建Notification并通知系统更新
            val updatedNotification = notificationBuilder.build()
            notificationManager.notify(1, updatedNotification)
        }

        override fun onFinish() {
            setPrayerNotificationCounter()
        }
    }
    countDownTimer.start()
}

额外注意事项

  • 确保notificationBuilder是全局维护的实例,每次build()都会生成包含最新contentView的Notification对象;
  • 前台服务需设置合适的启动模式(如START_STICKY),避免被系统低内存杀死;
  • 可以通过Android Studio的Profiler监控内存变化,验证Bitmap回收是否生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:50:14