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

如何实现安卓应用在未被使用一天后发送通知?现有代码无法运行求改进方案

嗨,我帮你排查下代码里的问题,一步步教你实现「用户一天未打开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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:17:46