setExactAndAllowWhileIdle() for alarmmanager is no

2019-02-14 08:49发布

问题:

I am developing an app which needs to perform particular action on the exact time which user has set. For this i am using setExactAndAllowWhileIdle() method because this documentation says that android devices having android 6.0 or above has doze mode concept in which if devices remains idle for some times then it will enter into doze mode and doze mode restricts alarms. If i want to fire my alarm when device is into doze mode then i have setExactAndAllowWhileIdle() method as documentation says. This documentation also contains manual way to enter device into doze mode for testing purpose. so, i am testing using that way but my alarm is not fired when device is into doze mode and when i stops doze mode via terminal command my past alarm will fires instantly.

So, my problem is that setExactAndAllowWhileIdle() this method is not working in doze mode but it should have to work as said in documentation. I know limitation of this method that i can only fire one alarm per 9 minutes and i am following this rule. So, i can't understand where is the problem.

My Code:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC, d.getTime(), pendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    alarmManager.setExact(AlarmManager.RTC, d.getTime(), pendingIntent);
else
    alarmManager.set(AlarmManager.RTC, d.getTime(), pendingIntent);

Is it a method problem or i am doing it in a wrong way??

回答1:

I've found the solution for my problem so, i am posting my own answer here which worked for me.

Using setAlarmClock() method has solved my problem. If you set alarm using setAlarmClock() method then this will not allow the system to go into doze mode before 1 hour of your alarm's time. I had tested this by manually forcing my device to go into doze mode after setting my alarm. Let me explain full scenario.

  1. First of all i set alarm after 5 minutes from current time and then tried to putting my device into doze mode manually by using following command.

adb shell dumpsys deviceidle force-idle

It Shows

unable to enter into doze mode

  1. After that i set alarm after 1 hour and 1 minute from current time and then i have tried to put my device into doze mode and it enters into doze mode successfully. Then i have done nothing on my device and it fires alarm exactly on time even it was in a doze mode.

Hence, i conclude that setAlarmClock() method prevents your device from entering to doze mode if there is a small amount of timestamp between current time and your alarm time. Otherwise if your device is already in doze mode then it will gets exit from doze mode before some time of your alarm so, your alarm works fine.

Updated code:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(d.getTime(),pendingIntent),pendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    alarmManager.setExact(AlarmManager.RTC, d.getTime(), pendingIntent);
else
    alarmManager.set(AlarmManager.RTC, d.getTime(), pendingIntent);


回答2:

According to documentation

RTC - Fires the pending intent at the specified time but does not wake up the device.

RTC_WAKEUP - Wakes up the device to fire the pending intent at the specified time.

Use AlarmManager.RTC_WAKEUP instead of AlarmManager.RTC



回答3:

you can whitelist your app from doze mode by ignoring battery optimizations..

Add permission

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

request whitelist your app

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Intent intent = new Intent();
            String packageName = getPackageName();
            PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
            if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + packageName));
                startActivity(intent);
            }
        }

Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 15 minutes per app.