如何实现安卓应用在未被使用一天后发送通知?现有代码无法运行求改进方案
嗨,我帮你排查下代码里的问题,一步步教你实现「用户一天未打开APP就发送通知」的功能:
现有代码的核心问题
你的代码目前有几个关键问题导致功能失效:
- Alarm触发逻辑错误:你用
setRepeating设置了从当前时间开始每天重复触发,这会不管用户有没有打开APP都发通知,完全不符合「未使用一天才触发」的需求。 - 组件拼写错误:广播接收器类名
NotificationReciver少了一个字母e,应该是NotificationReceiver,系统找不到这个接收器会导致广播无法触发。 - Android后台限制:Android 8.0及以上版本禁止后台直接调用
startService,会抛出异常,需要改用前台服务或JobIntentService。 - 缺少用户活跃跟踪:你没有记录用户最后一次打开APP的时间,无法判断是否满足「一天未使用」的条件。
完整修改方案
1. 跟踪用户最后一次活跃时间
在用户每次打开APP时,把当前时间存到SharedPreferences里,这样我们就能知道用户最后一次使用的时间:
@Override protected void onResume() { super.onResume(); // 记录最后活跃时间 SharedPreferences prefs = getSharedPreferences("AppUsage", MODE_PRIVATE); prefs.edit().putLong("last_active_time", System.currentTimeMillis()).apply(); // 更新Alarm,确保下次提醒时间是「最后活跃时间 + 1天」 setupReminderAlarm(); }
2. 修正AlarmManager的触发逻辑
替换原来的Alarm设置代码,改为根据用户最后活跃时间计算触发时间,同时适配Android 12+的PendingIntent权限:
private static final int NOTIFICATION_REMINDER_REQUEST_CODE = 3; private static final long REMINDER_INTERVAL = 24 * 60 * 60 * 1000; // 1天 private void setupReminderAlarm() { Context context = this; SharedPreferences prefs = getSharedPreferences("AppUsage", MODE_PRIVATE); // 获取最后活跃时间,默认值为当前时间 long lastActiveTime = prefs.getLong("last_active_time", System.currentTimeMillis()); // 计算下次触发时间:最后活跃时间 + 1天 long triggerTime = lastActiveTime + REMINDER_INTERVAL; Intent notifyIntent = new Intent(context, NotificationReceiver.class); // Android 12+必须设置IMMUTABLE或MUTABLE标志 int pendingFlags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_UPDATE_CURRENT; PendingIntent pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_REMINDER_REQUEST_CODE, notifyIntent, pendingFlags); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); if (alarmManager != null) { // 先取消之前的Alarm,避免重复设置 alarmManager.cancel(pendingIntent); // 使用setExactAndAllowWhileIdle保证Doze模式下也能触发(Android 6.0+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent); } else { alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent); } } }
3. 修正广播接收器与通知服务
修正广播接收器类名与逻辑
把NotificationReciver改为NotificationReceiver,并添加时间校验,同时适配后台启动限制:
public class NotificationReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { SharedPreferences prefs = context.getSharedPreferences("AppUsage", MODE_PRIVATE); long lastActiveTime = prefs.getLong("last_active_time", 0); // 再次校验是否确实超过1天未使用 if (System.currentTimeMillis() - lastActiveTime >= REMINDER_INTERVAL) { // Android 8.0+需要启动前台服务 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Intent serviceIntent = new Intent(context, NotificationIntentService.class); context.startForegroundService(serviceIntent); } else { context.startService(new Intent(context, NotificationIntentService.class)); } } // 设置下一次的提醒Alarm(如果用户还是没打开APP,第二天继续提醒) setupNextAlarm(context); } private void setupNextAlarm(Context context) { Intent setupIntent = new Intent(context, MainActivity.class); setupIntent.setAction("SETUP_REMINDER_ALARM"); setupIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(setupIntent); } }
适配通知服务的Android版本限制
修改NotificationIntentService,添加通知渠道(Android 8.0+必须)和前台服务处理:
public class NotificationIntentService extends IntentService { private static final int NOTIFICATION_ID = 1001; private static final int FOREGROUND_SERVICE_ID = 1002; private static final String REMINDER_CHANNEL_ID = "REMINDER_CHANNEL"; public NotificationIntentService() { super("NotificationIntentService"); } @Override protected void onHandleIntent(Intent intent) { // Android 8.0+需要先启动前台服务 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel(); Notification foregroundNotification = new Notification.Builder(this, REMINDER_CHANNEL_ID) .setContentTitle("后台监控") .setContentText("正在跟踪APP使用情况") .setSmallIcon(R.drawable.ic_splash_screen) .build(); startForeground(FOREGROUND_SERVICE_ID, foregroundNotification); } // 创建提醒通知 Notification.Builder builder = new Notification.Builder(this) .setContentTitle("D4rK Cleaner!!!") .setContentText("You didn't clean your phone for some time. Try now!") .setSmallIcon(R.drawable.ic_splash_screen) .setAutoCancel(true); // 点击通知后自动消失 Intent notifyIntent = new Intent(this, MainActivity.class); int pendingFlags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_UPDATE_CURRENT; PendingIntent pendingIntent = PendingIntent.getActivity(this, 2, notifyIntent, pendingFlags); builder.setContentIntent(pendingIntent); // Android 8.0+绑定通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(REMINDER_CHANNEL_ID); } Notification notification = builder.build(); NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification); // 停止前台服务(如果已启动) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { stopForeground(true); } } // 创建通知渠道(Android 8.0+) private void createNotificationChannel() { NotificationChannel channel = new NotificationChannel(REMINDER_CHANNEL_ID, "APP使用提醒", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager notificationManager = getSystemService(NotificationManager.class); if (notificationManager != null) { notificationManager.createNotificationChannel(channel); } } }
4. 配置Manifest文件
添加必要的权限和组件声明,包括开机重启Alarm的接收器:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application ...> <!-- 声明广播接收器 --> <receiver android:name=".NotificationReceiver" /> <!-- 声明通知服务 --> <service android:name=".NotificationIntentService" /> <!-- 开机重启后重新设置Alarm --> <receiver android:name=".BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application>
5. 添加开机重启Alarm的接收器
创建BootCompletedReceiver,确保设备重启后Alarm能重新生效:
public class BootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { // 重启后触发Alarm设置逻辑 Intent setupIntent = new Intent(context, MainActivity.class); setupIntent.setAction("SETUP_REMINDER_ALARM"); setupIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(setupIntent); } } }
最后在MainActivity的onCreate中处理这个开机后的设置请求:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 处理开机后的Alarm设置请求 if ("SETUP_REMINDER_ALARM".equals(getIntent().getAction())) { setupReminderAlarm(); } }
这样修改后,当用户打开APP时会记录活跃时间,Alarm会在「最后活跃时间+1天」触发,如果用户在这期间打开APP,会重新计算触发时间;如果超过1天未打开,就会发送提醒通知,并且设备重启后也能恢复Alarm设置。
内容的提问来源于stack exchange,提问作者Mihai-Cristian Condrea
相关产品推荐
相关产品推荐

