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

媒体通知中LargeIcon像素化求助:原图质量良好但显示模糊

媒体通知专辑封面LargeIcon像素化问题解决方法

问题描述

开发媒体通知时,专辑封面作为LargeIcon显示严重像素化,但三星音乐使用同一张封面的通知却保持高清。具体对比:

  • 我的通知:像素化封面
  • 三星音乐通知:高清封面
  • 我的播放器内:封面显示清晰
  • 三星音乐播放器内:封面显示清晰

已尝试的无效修复方案:

  • 通过MediaMetadata设置LargeIcon:通知无封面
  • 手动缩放Bitmap:仅改变封面位置,像素化问题未解决
  • 使用drawable目录本地图片作为LargeIcon:显示正常

无论播放歌曲类型、原图分辨率如何,LargeIcon均呈现像素化,需实现与三星音乐一致的高清通知封面效果。

相关代码

private fun buildNotification(song: Song, isPlaying: Boolean): Notification {
        val playPauseAction = if (isPlaying) {
            NotificationCompat.Action(
                R.drawable.ic_pause, "Pause",
                MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PAUSE)
            )
        } else {
            NotificationCompat.Action(
                R.drawable.ic_play_arrow, "Play",
                MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY)
            )
        }

        val style = androidx.media.app.NotificationCompat.MediaStyle()
            .setShowActionsInCompactView(0)
            .setMediaSession(mediaSession.sessionToken)

//        val metadataBuilder = MediaMetadataCompat.Builder()
//            .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.songName)
//            .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.songArtist)
//            .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getBitmapFromUri(song.songCover)) // used as largeIcon
//
//        mediaSession.setMetadata(metadataBuilder.build())

        return NotificationCompat.Builder(this, "shufflo_music_channel")
            .setContentTitle(song.songName)
            .setContentText(song.songArtist)
            .setLargeIcon(getBitmapFromUri(song.songCover))
            //.setLargeIcon(
            //    BitmapFactory.decodeResource(getResources(), R.drawable.test).scale(128, 128, false))
            .setSmallIcon(R.drawable.ic_album_art_placeholder)
            .addAction(playPauseAction)
            .setStyle(style)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setOnlyAlertOnce(true)
            .setOngoing(isPlaying) // important for making it non-dismissible
            .build()
    }

    private fun getBitmapFromUri(uri: Uri): Bitmap? {
        return try {
            val inputStream = contentResolver.openInputStream(uri)
            val original = BitmapFactory.decodeStream(inputStream)
            inputStream?.close()
            original
            //original?.scale(128, 128, false)
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

问题根源

直接用BitmapFactory.decodeStream解码Uri得到的Bitmap未适配设备屏幕密度(DPI),通知系统缩放时导致像素化。三星音乐这类应用会根据LargeIcon最优尺寸对原图做采样和缩放,同时匹配屏幕密度,因此显示清晰。

修复方案

1. 获取通知LargeIcon最优尺寸

通知LargeIcon推荐尺寸为192x192dp,可通过系统资源获取对应像素值:

private fun getNotificationLargeIconSize(context: Context): Int {
    val res = context.resources
    return res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width)
}

2. 优化Bitmap解码流程

修改getBitmapFromUri方法,通过BitmapFactory.Options采样,避免加载过大Bitmap,同时保证缩放清晰度:

private fun getBitmapFromUri(uri: Uri, context: Context): Bitmap? {
    return try {
        var inputStream = context.contentResolver.openInputStream(uri) ?: return null

        // 先获取原图尺寸,不加载到内存
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeStream(inputStream, null, options)
        inputStream.close()

        // 计算采样率,适配目标尺寸
        val targetSize = getNotificationLargeIconSize(context)
        options.inSampleSize = calculateInSampleSize(options, targetSize, targetSize)
        options.inJustDecodeBounds = false
        // 开启DPI匹配,保证缩放适配屏幕
        options.inScaled = true
        options.inDensity = options.outWidth
        options.inTargetDensity = context.resources.displayMetrics.densityDpi

        inputStream = context.contentResolver.openInputStream(uri)
        val bitmap = BitmapFactory.decodeStream(inputStream, null, options)
        inputStream?.close()
        bitmap
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

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. 更新通知构建代码

在buildNotification中调用优化后的方法:

.setLargeIcon(getBitmapFromUri(song.songCover, this))

额外注意事项

  • 避免直接使用Bitmap.scale方法,该方法缩放算法优化不足,解码阶段处理效率更高、清晰度更好。
  • 若需通过MediaSession传递封面,确保METADATA_KEY_ALBUM_ART使用优化后的Bitmap,且MediaSession已正确关联到通知的MediaStyle。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:42:02