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

Android提醒通知无法显示问题排查与代码求助

Android提醒通知无法触发问题排查请求

我开发了一款Android应用,功能是根据用户输入内容创建提醒通知,需在指定的未来日期时间显示,且应用未启动时也能触发提醒。但设置提醒后,通知窗口始终无法出现。我已在AndroidManifest.xml中添加了必要权限,查阅资料后修改了部分代码(如下),但问题仍未解决,请求协助排查。


初始代码

MainActivity.kt

package com.example.kalendarz_2
import android.app.AlarmManager
import android.app.DatePickerDialog
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TimePickerDialog
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.DatePicker
import android.widget.EditText
import android.widget.TimePicker
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.util.*

class MainActivity : AppCompatActivity() {

    private lateinit var btnSetReminder: Button
    private lateinit var etReminderText: EditText

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnSetReminder = findViewById(R.id.btnSetReminder)
        etReminderText = findViewById(R.id.etReminderText)
        
    }

    fun showDateTimePicker(view: View) {
        val calendar = Calendar.getInstance()
        val initialYear = calendar.get(Calendar.YEAR)
        val initialMonth = calendar.get(Calendar.MONTH)
        val initialDay = calendar.get(Calendar.DAY_OF_MONTH)
        val initialHour = calendar.get(Calendar.HOUR_OF_DAY)
        val initialMinute = calendar.get(Calendar.MINUTE)

        val datePickerDialog = DatePickerDialog(this, { _, year, month, day ->
            val timePickerDialog = TimePickerDialog(this, { _, hourOfDay, minute ->
                setReminder(year, month, day, hourOfDay, minute)
            }, initialHour, initialMinute, true)
            timePickerDialog.show()
        }, initialYear, initialMonth, initialDay)

        datePickerDialog.show()
    }

    private fun setReminder(year: Int, month: Int, day: Int, hourOfDay: Int, minute: Int) {
        val calendar = Calendar.getInstance()
        calendar.set(year, month, day, hourOfDay, minute)

        if (calendar.timeInMillis > System.currentTimeMillis()) {
            val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val intent = Intent(this, ReminderReceiver::class.java)
            intent.putExtra("REMINDER_TEXT", etReminderText.text.toString())
            intent.putExtra("REMINDER_TIME", calendar.timeInMillis)

            val pendingIntent = PendingIntent.getService(
                this, 0, intent,
                PendingIntent.FLAG_IMMUTABLE
            )

            alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
            Toast.makeText(this, "提醒设置于: ${calendar.time}", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(this, "请选择未来的日期和时间", Toast.LENGTH_SHORT).show()
        }
    }
}

ReminderService.kt

package com.example.kalendarz_2

import android.app.*
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.annotation.RequiresApi
import java.util.*

class ReminderService : Service() {

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("ReminderService", "提醒服务已启动")
        showReminderNotification(intent?.getStringExtra("REMINDER_TEXT") ?: "无提醒内容")

        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun showReminderNotification(reminderText: String) {
        val notificationManager = getSystemService(NotificationManager::class.java)

        val notificationChannelId = "channel_id"
        val notificationBuilder = Notification.Builder(this, notificationChannelId)
            .setContentTitle("通知")
            .setContentText("通知内容: $reminderText")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setAutoCancel(true)

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                notificationChannelId,
                "通知",
                NotificationManager.IMPORTANCE_HIGH
            )
            notificationManager?.createNotificationChannel(notificationChannel)
            notificationBuilder.setChannelId(notificationChannelId)
        }

        notificationManager?.notify(1, notificationBuilder.build())
    }
}

ReminderReceiver.kt

package com.example.kalendarz_2

import android.app.AlertDialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.util.Log
import android.widget.Toast

class ReminderReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("ReminderReceiver", "收到提醒广播")
        val reminderText = intent?.getStringExtra("REMINDER_TEXT") ?: "无提醒内容"
        val reminderTime = intent?.getLongExtra("REMINDER_TIME", 0) ?: 0

        val currentTime = System.currentTimeMillis()

        if (reminderTime > currentTime) {
            val serviceIntent = Intent(context, ReminderService::class.java)
            serviceIntent.putExtra("REMINDER_TEXT", reminderText)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context?.startForegroundService(serviceIntent)
            } else {
                context?.startService(serviceIntent)
            }
        } else {
            Toast.makeText(context, "通知: $reminderText", Toast.LENGTH_LONG).show()
        }
    }
}

activity_main.xml

<!-- res/layout/activity_main.xml -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingTop="16dp"
    android:paddingRight="16dp"
    android:paddingBottom="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnSetReminder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="设置提醒"
        android:layout_centerInParent="true"
        android:onClick="showDateTimePicker" />

    <EditText
        android:id="@+id/etReminderText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnSetReminder"
        android:layout_marginTop="16dp"
        android:hint="提醒内容"
        android:padding="16dp"
    />
</RelativeLayout>

AndroidManifest.xml权限

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

修改后的代码片段

MainActivity.kt的setReminder方法

private fun setReminder(year: Int, month: Int, day: Int, hourOfDay: Int, minute: Int) {
    val calendar = Calendar.getInstance()
    calendar.set(year, month, day, hourOfDay, minute)

    if (calendar.timeInMillis > System.currentTimeMillis()) {
        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val intent = Intent(this, ReminderReceiver::class.java)
        intent.putExtra("REMINDER_TEXT", etReminderText.text.toString())
        intent.putExtra("REMINDER_TIME", calendar.timeInMillis)

        val pendingIntent = PendingIntent.getBroadcast(
            this, 0, intent,
            PendingIntent.FLAG_IMMUTABLE
        )

        alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        Toast.makeText(this, "通知设置于: ${calendar.time}", Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(this, "请选择未来的日期", Toast.LENGTH_SHORT).show()
    }
}

ReminderReceiver.kt

class ReminderReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("ReminderReceiver", "收到提醒广播")
        val reminderText = intent?.getStringExtra("REMINDER_TEXT") ?: "无通知内容"
        val reminderTime = intent?.getLongExtra("REMINDER_TIME", 0) ?: 0

        val currentTime = System.currentTimeMillis()

        if (reminderTime > currentTime) {
            showReminderNotification(context, reminderText)
        } else {
            Toast.makeText(context, "通知: $reminderText", Toast.LENGTH_LONG).show()
        }
    }

    private fun showReminderNotification(context: Context?, reminderText: String) {
        val notificationManager = context?.getSystemService(NotificationManager::class.java)

        val notificationChannelId = "channel_id"
        val notificationBuilder = NotificationCompat.Builder(context!!, notificationChannelId)
            .setContentTitle("通知")
            .setContentText("通知内容: $reminderText")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setAutoCancel(true)
            .setDefaults(Notification.DEFAULT_SOUND) 

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                notificationChannelId,
                "通知",
                NotificationManager.IMPORTANCE_HIGH
            )
            notificationManager?.createNotificationChannel(notificationChannel)
            notificationBuilder.setChannelId(notificationChannelId)
        }
        
        notificationManager?.notify(1, notificationBuilder.build())
    }
}

问题排查与修复方案

1. 注册BroadcastReceiver

AndroidManifest.xml中未注册ReminderReceiver,系统无法识别该广播接收器,导致闹钟触发后无响应。在<application>标签内添加:

<receiver android:name=".ReminderReceiver" android:exported="false" />

2. 移除Receiver中多余的时间判断

闹钟触发时,reminderTime就是设定的触发时间,此时currentTime必然大于等于reminderTime,导致通知逻辑无法执行。修改onReceive方法:

override fun onReceive(context: Context?, intent: Intent?) {
    Log.d("ReminderReceiver", "收到提醒广播")
    val reminderText = intent?.getStringExtra("REMINDER_TEXT") ?: "无提醒内容"
    // 直接显示通知,无需再判断时间
    showReminderNotification(context, reminderText)
}

3. 主动申请通知权限(Android 13+)

仅在Manifest声明POST_NOTIFICATIONS权限不够,需在MainActivity中主动申请:

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

// 在onCreate方法中添加
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    btnSetReminder = findViewById(R.id.btnSetReminder)
    etReminderText = findViewById(R.id.etReminderText)
    
    // 申请通知权限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 101)
        }
    }
}

4. 使用唯一的PendingIntent请求码

当前请求码固定为0,多个提醒会互相覆盖。改用时间戳作为请求码:

val requestCode = calendar.timeInMillis.toInt()
val pendingIntent = PendingIntent.getBroadcast(
    this, requestCode, intent,
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

5. 适配AlarmManager精准触发(Android 12+)

alarmManager.set()可能被系统优化延迟,改用精准触发API:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
} else {
    alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
}

6. 检查通知图标有效性

确保R.mipmap.ic_launcher是符合要求的图标,建议使用透明背景的专用通知图标(放置在res/drawable目录下)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:35:55