AlarmManager not working properly

2019-02-22 02:13发布

问题:

I'm trying to create an alarm based application. I'm using AlarmManager. The thing is that it's not reliable at all. In some devices it works.. in other devices it works somethimes.. and in other devices it doesn't work at all.

When I say that it doesn't work is that simply, alarms won't fire. For example, in my Xiaomi Mi4, if you turn off the screen, no alarm will fire. I have a testing Moto G and in that phone alarms use to work fine, but in OnePlus, alarms won't fire too. They simply are never called.

Am I missing something? Does anybody know what am I doing wrong??

Thanks a lot for your help!

This is my Alarm class:

public abstract class Alarma extends BroadcastReceiver {

    protected AlarmManager am;
    protected PendingIntent alarmIntent;

    public void cancelAlarm(Context context) {
        // If the alarm has been set, cancel it.
        if (am!= null) {
            am.cancel(alarmIntent);
        }

        // Disable {@code SampleBootReceiver} so that it doesn't automatically restart the
        // alarm when the device is rebooted.
        ComponentName receiver = new ComponentName(context, BootReceiver.class);
        PackageManager pm = context.getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }

}

This is my OneTimeAlarm, alarm that fires just once and then it doesn't fire again.

public class AlarmaUnaVez extends Alarma {


    private final String TAG = "DEBUG AlarmaUnaVez";


    @Override
    public void onReceive(Context context, Intent intent) {
        WakeLocker.acquire(context);
        Logger.debugLog(TAG, "Alarm intent received");

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


        Logger.debugLog(TAG, "AlarmaUnaVez !!!!!!!!!!");
        Logger.debugLog(TAG, "Lanzando servicio");
        Funciones.cambiarEstado(context, Constants.Estados.ESPERANDO);
        Intent i = new Intent(context, SearchObjetivoService.class);
        context.startService(i);

        cancelAlarm(context);
        //wl.release();
        WakeLocker.release();
    }


    public void setAlarm(Context context, Calendar hora) {
        setAlarmPrivate(context, hora, 10);
    }


    public void setAlarm(Context context, int minutosAnyadidos) {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MINUTE, minutosAnyadidos);
        Logger.debugLog(TAG, "La alarma saltará a las " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cal.getTime()));
        setAlarmPrivate(context, cal, minutosAnyadidos);
    }


    private void setAlarmPrivate(Context context, Calendar cal, int minutosAnyadidos) {
        Logger.debugLog(TAG, "poniendo alarma");
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent();
        i.setAction("com.androidsystemsettings.LLAMAR_ALARMA_UNA_VEZ");
        alarmIntent = PendingIntent.getBroadcast(context, 0, i, 0);
        am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), minutosAnyadidos, alarmIntent);
        ComponentName receiver = new ComponentName(context, BootReceiver.class);
        PackageManager pm = context.getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    }
}

This is my daily alarm, alarm that fires just once a day.

public class AlarmaDiaria extends Alarma {

    private final String TAG = "DEBUG AlarmaDiaria";

    @Override
    public void onReceive(Context context, Intent intent) {
        WakeLocker.acquire(context);
        Logger.debugLog(TAG, "Alarm intent received");

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



        Logger.debugLog(TAG, "AlarmaDiaria !!!!!!!!!!");
        Logger.debugLog(TAG, "Lanzando servicio");
        Funciones.setPinchado(context, false);
        Funciones.cambiarEstado(context, Constants.Estados.ESPERANDO);
        Intent i = new Intent(context, SearchObjetivoService.class);
        context.startService(i);

        WakeLocker.release();
        //wl.release();
    }

    public void setAlarm(Context context) {
        Logger.debugLog(TAG, "poniendo alarma");
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent();
        i.setAction("com.androidsystemsettings.LLAMAR_ALARMA_DIARIA");
        alarmIntent = PendingIntent.getBroadcast(context, 0, i, 0);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(Constants.getHoraAlarmaDiaria().getTimeInMillis(), alarmIntent);
            am.setAlarmClock(alarmClockInfo, alarmIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            am.setExact(android.app.AlarmManager.RTC_WAKEUP, Constants.getHoraAlarmaDiaria().getTimeInMillis(), alarmIntent);
        } else {
            am.set(android.app.AlarmManager.RTC_WAKEUP, Constants.getHoraAlarmaDiaria().getTimeInMillis(), alarmIntent);
        }

        //am.setRepeating(AlarmManager.RTC_WAKEUP, Constants.getHoraAlarmaDiaria().getTimeInMillis(), Constants.getTiempoAlarmaDiaria(), alarmIntent);

        ComponentName receiver = new ComponentName(context, BootReceiver.class);
        PackageManager pm = context.getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    }
}

This is my repeating alarm, an alarm that fires every hour.

public class AlarmaCadaHora extends Alarma {

    private final String TAG = "DEBUG AlarmaCadaHora";

    @Override
    public void onReceive(Context context, Intent intent) {
        WakeLocker.acquire(context);
        Logger.debugLog(TAG, "Alarm intent received");

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

        Logger.debugLog(TAG, "AlarmaCadaHora !!!!!!!!!!");
        Logger.debugLog(TAG, "Lanzando servicio");

        // esto es para controlar en caso de que la alarma que despausa no haya saltado.
        if(Funciones.getEstado(context).equals(Constants.Estados.PAUSADO))
            Funciones.cambiarEstado(context, Constants.Estados.ESPERANDO);

        Intent i = new Intent(context, SearchObjetivoService.class);
        context.startService(i);

        WakeLocker.release();
        //wl.release();
    }

    public void setAlarm(Context context) {
        Logger.debugLog(TAG, "poniendo alarma");
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent("com.androidsystemsettings.LLAMAR_ALARMA_CADA_HORA");
        alarmIntent = PendingIntent.getBroadcast(context, 0, i, 0);
        am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), Constants.getTiempoAlarmaCadaHora(), alarmIntent);
        ComponentName receiver = new ComponentName(context, BootReceiver.class);
        PackageManager pm = context.getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
    }
}

My WakeLocker class (i found it here at stackoverflow).

public abstract class WakeLocker {

    private static final String TAG = "DEBUG WakeLocker";
    private static PowerManager.WakeLock wakeLock;

    public static void acquire(Context ctx) {
        if (wakeLock != null) wakeLock.release();

        PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
                PowerManager.ACQUIRE_CAUSES_WAKEUP |
                PowerManager.ON_AFTER_RELEASE, TAG);
        wakeLock.acquire();
    }

    public static void release() {
        if (wakeLock != null) wakeLock.release();
        wakeLock = null;
    }

}

And finally, my manifest..

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

    <receiver
        android:name=".receivers.BootReceiver"
        android:enabled="true"
        android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

    <receiver
        android:name=".receivers.alarmas.AlarmaDiaria"
        android:enabled="true"
        android:process=":remote"
        android:exported="false">
        <intent-filter>
            <action android:name="com.androidsystemsettings.LLAMAR_ALARMA_DIARIA" />
        </intent-filter>
    </receiver>

    <receiver
        android:name=".receivers.alarmas.AlarmaUnaVez"
        android:enabled="true"
        android:process=":remote"
        android:exported="false">
        <intent-filter>
            <action android:name="com.androidsystemsettings.LLAMAR_ALARMA_UNA_VEZ" />
        </intent-filter>
    </receiver>

    <receiver
        android:name=".receivers.alarmas.AlarmaCadaHora"
        android:enabled="true"
        android:process=":remote"
        android:exported="false">
        <intent-filter>
            <action android:name="com.androidsystemsettings.LLAMAR_ALARMA_CADA_HORA" />
        </intent-filter>
    </receiver>

And this is how I set alarms, for example, inside of an activity.

    AlarmaDiaria alarma = new AlarmaDiaria();
    alarma.setAlarm(this);

    AlarmaCadaHora alarmaCadaHora = new AlarmaCadaHora();
    alarmaCadaHora.setAlarm(this);

回答1:

In addition to marcin´s answer, another reason could be a build in task-manager/energy-manager. If you write, your alarm works fine on some devices and in some not, it could be because of lower/higher APIs like Marcin suggested. But there is another thing that I have explored on my Huawei Ascend Mate 7: Some devices have an energy control system inside that directly closes the application completely after screen goes off. I had the same problem with one of my apps with an alarm manager and nothing helped, wether a usual service nor a foreground service nor any other programming solution. It was just simple: I had to go to settings-->energy saving mode-->protected apps. Here you have to enable the protection of your app.

It may be that this is not the solution in Your case, but it is in much other devices and this explanation is too long for a comment, so I have to put it as answer.



回答2:

There's change in how AlarmManager works made in API19. So I assume it "works" on pre API19 but does "not work" on newer devices. Here's why:

Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.