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

在Android实现类iOS风格通知的技术求助

问题描述
  • 用Kotlin开发Android应用,接收服务器推送的data消息,需实现类iOS风格通知:
    • 手机亮屏/解锁状态(当前应用、其他应用或桌面):弹出heads-up提示消息
    • 手机熄屏/锁屏状态:通知栏显示展开式通知并点亮屏幕
  • 现有实现问题:
    • 用LockscreenActivity实现亮屏会全屏显示,不符合需求
    • 不使用该Activity则无法点亮屏幕
    • 点击通知跳转MainActivity时,setShowWhenLocked(true)和setTurnScreenOn(true)无效
最简解决方案

无需额外的LockscreenActivity,通过以下步骤实现需求:

1. 添加必要权限

在AndroidManifest.xml中添加唤醒屏幕和通知相关权限:

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISPLAY_NOTIFICATION_POLICY" />
<!-- Android 14+ 需添加 -->
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />

2. 配置MainActivity

在Manifest中设置Activity属性

<activity
    android:name=".MainActivity"
    android:showWhenLocked="true"
    android:turnScreenOn="true"
    android:excludeFromRecents="false">
    <!-- 其他原有配置 -->
</activity>

补充MainActivity代码

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    // 适配不同Android版本的锁屏显示与唤醒
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.insetsController?.let {
            it.show(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
            it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        }
    } else {
        @Suppress("DEPRECATION")
        window.addFlags(
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
            or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
            or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
        )
    }
    
    setContentView(R.layout.activity_main)
}

3. 修改NotificationService代码

移除全屏Intent相关逻辑,改用WakeLock点亮屏幕,优化通知配置:

class NotificationService : FirebaseMessagingService() {

    private val TAG = "NotificationService"

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        // 优先取data消息,其次取notification消息
        val messageBody = remoteMessage.data["my_alert"] ?: remoteMessage.notification?.body ?: return
        sendNotification(messageBody)
    }

    private fun sendNotification(messageBody: String) {
        val channelId = getString(R.string.default_notification_channel_id)
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

        // 创建通知渠道(仅Android O+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "My alerts",
                NotificationManager.IMPORTANCE_HIGH // 必须设为HIGH才能触发heads-up
            ).apply {
                description = "Notifications description."
                setShowBadge(false)
                lockscreenVisibility = Notification.VISIBILITY_PUBLIC
                enableVibration(true)
                vibrationPattern = longArrayOf(200, 200, 200, 200, 200)
                enableLights(true) // 可选:开启通知指示灯
            }
            notificationManager.createNotificationChannel(channel)
        }

        // 配置跳转MainActivity的PendingIntent
        val contentIntent = Intent(this, MainActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val contentPendingIntent = PendingIntent.getActivity(
            this,
            0,
            contentIntent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )

        // 构建通知,添加展开式样式
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(getString(R.string.alert_title))
            .setContentText(messageBody)
            .setStyle(NotificationCompat.BigTextStyle().bigText(messageBody)) // 展开式通知
            .setSound(defaultSoundUri)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setOnlyAlertOnce(true)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setContentIntent(contentPendingIntent)
            .setAutoCancel(true)

        // 点亮屏幕(无需额外Activity)
        wakeUpScreen()

        // 发送通知(用时间戳做ID避免重复覆盖)
        val notificationId = System.currentTimeMillis().toInt()
        notificationManager.notify(notificationId, notificationBuilder.build())
    }

    private fun wakeUpScreen() {
        val powerManager = getSystemService(POWER_SERVICE) as PowerManager
        val wakeLock = powerManager.newWakeLock(
            PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
            "${packageName}:notification_wake_lock"
        )
        wakeLock.acquire(10000) // 保持亮屏10秒,可按需调整时长
    }
}

关键说明

  • Heads-up触发:通知渠道IMPORTANCE_HIGH+通知PRIORITY_HIGH,系统会自动在亮屏状态下弹出heads-up提示
  • 亮屏实现:通过PowerManager的WakeLock直接唤醒屏幕,避免额外Activity导致的全屏问题
  • 锁屏跳转:通过Manifest和代码双配置showWhenLocked/turnScreenOn,确保跳转时直接解锁显示应用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:04:54