You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

安卓每日定时通知实现求助:小米6中AlarmManager与Service失效

解决小米Mi6上每日8点自动通知失效的问题

兄弟,我太懂你在小米手机上踩的这个坑了!我之前在Mi8上也遇到过一模一样的情况——明明AlarmManager和Service的代码逻辑看起来没问题,一退应用或者锁屏,直接就罢工了。核心原因就是小米的极致省电策略+后台进程限制,它会主动清理非白名单的后台进程,普通的AlarmManager和Service根本扛不住。下面给你一套亲测有效的解决方案:

一、修复AlarmManager的触发逻辑

普通的set()或者setRepeating()方法在系统进入Doze模式(锁屏一段时间后)会被延迟甚至忽略,必须用系统允许在Doze下触发的API:

  • 单次触发用setExactAndAllowWhileIdle()
  • 重复触发的话,因为setRepeating()不支持Doze模式,所以可以在每次触发后重新设置下一次的闹钟

给你个设置每日8点闹钟的代码示例:

private void setDailyAlarm(Context context) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmReceiver.class); // 自定义广播接收器
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

    // 设置每日8点的时间
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 8);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);

    // 如果当前时间已经过了8点,设置到第二天的8点
    if (calendar.getTimeInMillis() < System.currentTimeMillis()) {
        calendar.add(Calendar.DAY_OF_MONTH, 1);
    }

    // 适配Android 6.0及以上的Doze模式
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else {
        alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    }
}

然后在AlarmReceiveronReceive()方法里启动你的NotificationService:

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 启动前台服务
        Intent serviceIntent = new Intent(context, NotificationService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(serviceIntent);
        } else {
            context.startService(serviceIntent);
        }
    }
}

二、把Service改成前台服务

普通Service在后台很容易被小米杀掉,必须改成前台服务,系统会给它更高的优先级,不会轻易终止。下面修正你的NotificationService:

public class NotificationService extends Service {
    private static final int NOTIFICATION_ID = 1001;
    private static final String CHANNEL_ID = "daily_notification_channel";

    final Handler handler = new Handler();

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 创建通知渠道(适配Android 8.0+)
        createNotificationChannel();
        // 把服务设为前台服务
        startForeground(NOTIFICATION_ID, createNotification());
        // 这里执行你的通知逻辑
        showDailyNotification();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY; // 服务被杀死后尝试重启
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "每日通知", NotificationManager.IMPORTANCE_LOW);
            channel.setDescription("每日8点的提醒通知");
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }

    private Notification createNotification() {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_notification) // 替换成你的图标
                .setContentTitle("后台运行中")
                .setContentText("正在为你准备每日提醒")
                .setPriority(NotificationCompat.PRIORITY_LOW);
        return builder.build();
    }

    private void showDailyNotification() {
        handler.post(() -> {
            // 这里写你显示每日通知的逻辑
            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setSmallIcon(R.drawable.ic_notification)
                    .setContentTitle("每日提醒")
                    .setContentText("现在是8点啦!")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);

            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
            notificationManager.notify(NOTIFICATION_ID + 1, notificationBuilder.build());

            // 显示完通知后可以停止前台服务(可选,如果你不需要一直前台运行)
            // stopForeground(true);
            // stopSelf();
        });
    }
}

三、关键:给应用开小米后台白名单

就算代码写得再完美,小米的系统限制还是会拦,必须手动把应用加入白名单:

  • 打开「设置」→「应用管理」→找到你的应用→「权限管理」→「自启动管理」,开启允许自启动
  • 打开「设置」→「省电与电池」→「应用省电策略」→找到你的应用→选择「无限制」(不要选智能省电或极致省电)

四、额外适配:Android 12+的通知权限

如果你的应用目标SDK是31及以上,还要在Manifest里申请POST_NOTIFICATIONS权限,并且在运行时请求用户授权:

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

然后在启动页或者设置里请求权限:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
        ActivityResultContracts.RequestPermission requestPermission = new ActivityResultContracts.RequestPermission();
        registerForActivityResult(requestPermission, isGranted -> {
            // 处理权限申请结果
        }).launch(Manifest.permission.POST_NOTIFICATIONS);
    }
}

按照上面的步骤来,你的每日8点通知应该就能稳定工作了,亲测在Mi6上有效!

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

火山引擎 最新活动