AlarmManager - 设置为 10 分钟时不会关闭

AlarmManager - Won't go off when set to 10 minutes

这段代码的工作原理基本上是......它从初始计算开始,然后在 X 分钟后使用 alarmmanager 启动服务,该服务获得结束计算并确定两者之间的差异。在每个警报结束时,都会为相同的时间间隔设置一个新的警报。当我将时间间隔设置为 10 分钟时,闹钟根本不响。

我假设这与手机 CPU 进入睡眠状态有关,因为这是我几天来一直试图用许多不同的方法来编写此算法来解决的问题。

如果有人知道这是为什么,我们将不胜感激,因为这对我的程序的功能非常重要。作为旁注,这段代码可以完美运行长达 5 分钟的短时间间隔,一旦我插入 10,我什么也没有得到。

来自我的服务

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    // Set return value
    int retVal = START_NOT_STICKY;

    if (intent != null) {

        System.out.println("Service Thread: " + Thread.currentThread().getName());

        // Set up context reference for getObject
        self = this;

        // Set up global intent reference
        theIntent = intent;

        // Get data
        getData();

        // Enter foreground state
        String title = "The service has been started...";
        String subject = "Service is running.";
        String body = "Monitoring your battery usage...";
        Notification notification = new Notification(R.drawable.theicon, title,
                System.currentTimeMillis());
        if (notificationSounds)
            notification.defaults |= Notification.DEFAULT_SOUND;
        else
            notification.sound = null;
        Intent notificationIntent = new Intent(this, MainActivity3.class);
        PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        notification.setLatestEventInfo(this, subject, body, pendIntent);
        startForeground(1500, notification);

        // Calculate wait time (convert from minutes to ms)
        int waitTime = interval * 60000;
       // int waitTime = 15000; // Debug 15 second wait

        // Get initial battery
        int initialBatt = getBatteryPercent();

        // Debug
        System.out.println("Initial battery percent: " + initialBatt);

        // Get current time
        Calendar c = Calendar.getInstance();
        Date dateNow = c.getTime();
        long timeNow = dateNow.getTime(); // Time in MS

        // Set up alarm manager to wait and then execute next step
        AlarmManager AM = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        svcIntent1 = new Intent(Service.getObject(), AlarmReceiver.class);
        svcIntent1.putExtra("timeToUse", timeToUse);
        svcIntent1.putExtra("interval", interval);
        svcIntent1.putExtra("rawTime", rawTime);
        svcIntent1.putExtra("initialBatt", initialBatt);
        svcIntent1.putExtra("sounds", notificationSounds);
        pendingIntent = PendingIntent.getBroadcast(Service.getObject(), 0, svcIntent1, PendingIntent.FLAG_UPDATE_CURRENT);

        // Set up the next alarm
        System.out.println("The current time is " + dateNow.
        SimpleDateFormat sdf = new SimpleDateFormat("MM-dd hh:mm:ss a");
        Toast.makeText(this, "The current time is " + sdf.format(dateNow), Toast.LENGTH_LONG).show();
        Date n = new Date();
        n.setTime(timeNow+waitTime);
        System.out.println("Next calculation will complete at " + sdf.format(n));
        Toast.makeText(this, "Next calculation will complete at " + sdf.format(n), Toast.LENGTH_LONG).show();
        AM.setExact(AlarmManager.RTC_WAKEUP, timeNow + waitTime, pendingIntent);
    }
    return retVal;
}

onReceive 在我的广播接收器中 class

@Override
public void onReceive(Context c, Intent intent) {
    if(intent != null){
        // Get app context
        context = Service.getObject();

        Toast.makeText(context, "Alarm broadcast received.", Toast.LENGTH_SHORT).show();

        // Set up the intent
        theIntent = intent;

        // Get the extra data
        getData();

        // Do calculations and get new initial battery level for next alarm
        int endBatt = calculateHelper(initialBatt);

        // Set up alarm manager to wait and then execute next step
        AlarmManager AM = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent svcIntent1 = new Intent(Service.getObject(), AlarmReceiver.class);
        svcIntent1.putExtra("timeToUse", timeToUse);
        svcIntent1.putExtra("interval", interval);
        svcIntent1.putExtra("rawTime", rawTime);
        svcIntent1.putExtra("initialBatt", endBatt);
        svcIntent1.putExtra("sounds", context.notificationSounds);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(Service.getObject(), 0, svcIntent1, PendingIntent.FLAG_UPDATE_CURRENT);

        // Get times
        long timeNow = dateNow.getTime();
        int waitTime = interval * 60000;


        // Debug stuff
        // int waitTime = 15000;
        SimpleDateFormat sdf = new SimpleDateFormat("MM-dd hh:mm:ss a");
        System.out.println("The current time is " + sdf.format(dateNow));
        Toast.makeText(context, "The current time is " + sdf.format(dateNow), Toast.LENGTH_LONG).show();
        Date n = new Date();
        n.setTime(timeNow+waitTime);
        System.out.println("Next calculation will complete at " + sdf.format(n));
        Toast.makeText(context, "Next calculation will complete at " + sdf.format(n), Toast.LENGTH_LONG).show();

        // Setting the next alarm
        AM.setExact(AlarmManager.RTC_WAKEUP, timeNow + waitTime, pendingIntent);
    }
    else
        notify0(10,"ERROR", "ERROR", "Intent is null", true);
}

在我的清单中...

    <receiver
        android:name=".AlarmReceiver"
        android:enabled="true"
        android:exported="true" >
    </receiver>


我的新代码(感谢 Larry Schiefer 的建议)

我还发现,即使 WakeLock 被 WakefulBroadcastReceiver 持有,如果 OS 关闭该服务,它将在没有唤醒锁的情况下重新启动。因此,有时需要获取额外的唤醒锁以在 onStartCommand 中进行处理。就我而言,这是必需的。

为我服务

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    // Acquire WakeLock
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Service WakeLock");
    wl.acquire();

    // Set up global intent reference
    theIntent = intent;

    if (theIntent != null) {
        getData();
        if (intent.getAction().equals("Start_Interval")) {
            doStart();
        }
        else if(intent.getAction().equals("End_Interval")){
            doEnd();
        }
    }
    else{
        Log.e("ERROR", "The intent is NULL inside of onStartCommand. Activity closed?");
        cancelAlarm();
        stopSelf();
    }

    // Release WakeLock
    wl.release();

    return START_STICKY;
}

我的 onReceive - 将 "extends BroadcastReceiver" 更改为 "extends WakefulBroadcastReceiver"

@Override
public void onReceive(Context c, Intent intent) {
    if(intent != null){

        // Debug
        Log.d("Debug","Alarm broadcast received.");

        // Set up the global intent reference
        theIntent = intent;

        // Set up service context reference
        context = Service.getObject();

        // Get data from the intent
        getData();

        // Set up the new intent
        Log.d("Debug", "Setting up new intent with context: " + c + " and class: " + Service.class);
        Intent service = new Intent(c, Service.class);
        service.putExtra("timeToUse", timeToUse);
        service.putExtra("interval", interval);
        service.putExtra("rawTime", rawTime);
        service.putExtra("initialBatt", initialBatt);
        service.setAction("End_Interval");

        // Wake up the service and complete this interval's calculations
         startWakefulService(c, service);
    }
    else {
        context.cancelAlarm();
        context.stopSelf();
    }
}

在您的 BroadcastReceiver 中使用 Log 而不是 Toast 来验证是否收到了广播。一旦系统进入低功耗状态,RTC_WAKEUP 将唤醒它,但只要您的 onReceive 正在执行。在那之后,系统将释放闹钟的唤醒锁,并且系统可以在屏幕出现之前返回睡眠状态,我们会显示您的吐司消息。如果您需要您的服务根据警报实际执行某些操作,则需要使用您自己的唤醒锁来协调您的接收器和服务,以确保系统保持正常运行(请参阅 WakefulBroadcastReceiver。)