AlarmManager在多个设备中不起作用


85

我的应用程序使用AlarmManager,并且从4年前就开始运行。但是我注意到某些设备开始出现故障。

我非常确定代码是正确的(因为我在装有Doze的设备上使用WakefulBroadcastReceiver和setExactAndAllowWhileIdle),因为它可以在Nexus设备上正常运行,但是在某些制造商的设备(华为,小米等)中却无法使用。

例如,华为设备具有一种可杀死应用程序的电池管理器,当应用程序被终止时,计划的警报将被取消。因此,在华为电池管理器中将应用设置为“受保护”即可解决该问题。

但是最近我注意到它不能在更多设备上使用:小米,三星(也许与新的“智能管理器”有关?)……这种行为似乎正在成为一种标准:杀死后台应用程序。

有人知道吗?有什么方法可以确保触发警报?

编辑:此问题是由不同制造商添加的“节电器”引起的。更多信息在这里:https : //dontkillmyapp.com/


8
制造商将应用程序的功耗归咎于应用程序,并且他们继续推销八核处理器,而八核处理器消耗的电量要多于八核处理器。他们是否认为仅添加内核会加快手机速度?
FrozenFire '16

1
@AviLevinshtein也许我误解了您的问题。我在活动中创建警报。然后,当警报响起时,将执行广播接收器,最后,将执行WakefulIntentService(来自@commonsware的类)。
塞尔吉奥·维德

2
@JFValdes我仍在寻找解决方案。AlarmManager在装有Vanilla Android的设备上完美运行。问题是制造商试图“增强” Android功能,并且破坏了AlarmManager ...制造商不应实现自己的“省电模式”,如果他们使用标准的Doze模式,则AlarmManager会完美运行...仍在寻找解决方案...
塞尔吉奥·维德斯

1
有什么解决办法吗?其他应用程序(如提醒或类似功能)如何做到这一点?除了setAlarm之外,还必须有另一个选项,用于警报,而不用于提醒
kv1dr

1
@SergioViudes我也遇到了与Xiomi设备进行跟踪的相同问题。并且如果我通过以下设置使我的应用程序不受节电限制的限制,而不是在4个设备中有3个可以正常工作,请执行以下设置-->转到电池->电源->应用程序电池保护程序->您的应用程序现在选择没有限制(背景设置),然后允许后台位置选项
伊姆兰汗赛菲

Answers:


17

我已经尝试好几个星期了。我什么都没找到。华为会在一段时间后杀死所有警报。如果我将应用程序放到电池保护程序中受保护的应用程序中,则无济于事。但是,如果我将应用程序的程序包名称更改为包含闹钟,时钟或日历之类的字词,则它在任何其他设备上的运行都绝对正常。我不知道Google如何为这种废话提供认证。我认为OEM不应以这种方式修改核心平台。我了解他们有自己的连击保护程序,如果用户不使用它,则在一段时间后会杀死该应用程序。但是,这也杀死了受保护的应用程序的警报。

同样,setAlarmClock()对于准确的定时警报也有帮助。但是不可能将其用于小部件更新之类的想法。

更新:软件包名称关键字的保护在当前的华为设备上已不起作用,这在2017年是正确的。


和我一样,我也尝试过,但是没有办法解决某些品牌的小米,OPPO和华为的问题。他们有时会杀死后台进程并发出警报以节省电池。
安迪·苏西洛

1
我有华为电话,将包裹名称更改为警报/日历无济于事。绕过此问题的唯一方法是从电话管理器中将您的应用添加到受保护的应用列表中
Ashish Pardhiye

9

问题是智能管理器。三星有一个电池管理器,有时会禁止某些应用在后台运行。当返回到应用程序时,它试图“恢复”,但完全禁用了该应用程序,或者可能每隔5分钟左右恢复一次(取决于三星的处理方式)。

由于没有Samsung Manager,因此可以在android的普通版本上使用。您还可以安装具有某些功能的自定义版本的android,以启用SM(取决于rom)。


我快要疯了,因为我没有三星设备可以测试它。我只知道我的应用程序的用户在告诉我什么。您是否知道问题是由于应用程序被杀死而导致AlarmManager无法正常工作?还是问题在于由于该管理器而导致闹钟响起时设备无法唤醒?
塞尔吉奥·维德

@SergioViudes最近,许多公司都在实施自己的公司。例如LG有一款与三星类似的手机,也许您的手机有一款?问题不在于警报,警报应用程序被推到完全不活动的状态。Smart Manager认为它只是您不需要的随机应用程序。我注意到某些应用程序可以超越它,也许某些应用程序已被智能管理器接受。
SA

1
@SergioViudes我有一个三星要测试,我可以告诉您,您可以从中得到很多。当智能管理器优化您的应用程序时,没有任何错误或任何错误,它只是消失了,类似于强制停止。尽管它仍然在最近的应用程序列表中
Tim

谢谢蒂姆。解决此问题,而不必从“智能”管理器中排除应用程序将非常有用。
塞尔吉奥·维德斯16/02/24

诸如xiaomi(miui),vivo和htc之类的设备默认情况下会将一堆权限设置为false,除非它是他们似乎确定自己的“受信任”应用程序列表中的应用程序(默认情况下,whatsapp,truecaller等受信任) )。这正成为编码员的噩梦
Desidigitalnomad

3

大多数现代Android设备都带有应用程序或机制,它们会自动尝试找出节省电量的方法,因此可能会杀死某些第三方应用程序。这可能会导致删除计划的任务和作业(例如,警报不响起,推送通知不起作用等)。在许多情况下,这种情况完全独立于Android的省电机制,在我的情况下,当我检测到某些设备型号时,我无法进行更多的电池优化,我将用户重定向到启动管理器以将我的应用列入白名单

您在每个模型的此链接中发现了应调用https://android-arsenal.com/details/1/6771的意图


2

对于<5.0设备使用AlarmManager,对于5.0+设备使用JobScheduler。我不能肯定地说JobScheduler不会受到制造商的恶作剧的影响,但是由于Android试图将人们从AlarmManager转移到JobScheduler上,所以对我来说似乎不太可能。

编辑:Google已经提出了针对该问题的第一方解决方案,称为WorkManager。它抽象了多个调度框架,并为设备使用了最合适的框架。


2
不幸的是,与AlarmManager类不同,使用JobScheduler时的计时不准确。在我的应用中,时间安排应该准确:(
Sergio Viudes

我试了一下,当屏幕关闭时,一些优化器(至少是三星)杀死了JobScheduler中所有待处理的任务。所以它也坏了。这发生在5.0。更新到6.0后,它可以正常工作,我想他们已经解决了。我还无法与其他制造商进行测试。
Sloy

对于确切的时间,您不能使用后台服务或计划的服务。您可以尝试前台服务,但这会为用户创建一个持久性通知(可能是不受欢迎的),并且某些电话内置了任务杀手,它们会自动销毁前台服务。WorkManager是最好的解决方案,但不幸的是不会提供确切的时间安排。
汤姆(Tom),


0

如今,大多数新手机都与某种电池/省电管理器捆绑在一起,它们可以执行您描述的相同操作。不算duboosters和干净的主人。

我认为您需要在应用程序/游戏商店列表中添加免责声明或常见问题解答,以表明该应用程序必须置于电池管理器应用程序之外才能正常运行。


应该有另一种方法...用户不会阅读免责声明。我认为三星手机不允许应用程序使用AlarmManager ...
Sergio Viudes

该警报将不会触发“准时”,但他们会eventuially
Gavriel

我会说,这(是)最有用的答案。我希望有一个更好的解决方案,但是硬件制造商正在破坏运行良好的原始Android。
2016年

0

我前一阵子停止使用AlarmManager ...更好,更稳定的替代方案

  1. 创建服务
  2. 为BOOT_COMPLETED注册一个BroadcastReceiver
  3. 从接收方处解雇您的服务
  4. 在您的服务内部启动一个新的Handler,每隔X分钟就会循环一次(Android-使用postDelayed()调用定期运行方法
  5. 检查执行任务的时间是否到了:现在-执行时间> 0(如何在Java中查找两个日期之间的差值的持续时间?
  6. 如果是的话..执行任务并停止处理程序

是的..这很痛苦..但是工作完成了没什么


3
感谢您的建议,但我想避免这种方法,因为使用AlarmManager不会消耗RAM或任何资源。而且,如果您的应用程序被终止,服务将停止,对吗?
塞尔吉奥·维德

我没有说这种方法是BOOLETPROOF,但至少它包含在不同的api版本中:)
ymz

为了可靠地工作,此解决方案可能还需要使用唤醒锁,这会消耗大量电池。
帕维尔Nadolski

我猜你在这个问题上是对的..唯一的问题是:最糟糕的是什么-代码不可靠或性能差?无论如何,我个人认为在某些情况下还有其他适合的锁定方式(例如:stackoverflow.com/questions/5346694/…
ymz

0

您在听BOOT_COMPLETED吗?重新启动设备后,您需要再次设置警报。


是。正如我所说,警报自2012年以来一直有效,直到现在。重新启动设备后,我会在BOOT_COMPLETED广播接收器中重新安排警报。
塞尔吉奥·维德斯

1
要求重新启动以使您的应用程序再次运行甚至不是解决办法的一半
蒂姆

1
@TimCastelijns根本不是我在说什么。如果重新启动设备,则必须再次设置通过警报管理器设置的所有警报。
泰勒·帕夫

@TylerPfaff是,但是设备重启与该问题无关
Tim

0

这些设备运行什么版本的Android?

从API 23开始,当操作系统一段时间不使用时,操作系统本身将进入低功耗空闲模式,并且在该模式下不会发出警报。但是,应用程序可以通过一种方式明确地说“无论电池使用情况如何,我现在都需要发出此警报”。新的AlarmManager方法称为setAndAllowWhileIdle()setExactAndAllowWhileIdle()

从您的描述看来,这可能不是导致某些OEM设备出现问题的特定原因,但这是所有使用Alarm Manager的开发人员都应注意的问题。

最后,使用作业计划程序的机制可以更好地解决警报管理器的许多用法。为了向后兼容,Play Services“ GCM网络管理器”实际上在功能上与Job Scheduler非常接近-它在较新版本的Android上内部使用Job Scheduler-尽管类的名称不一定与网络有关。


带有Smart Manager的三星设备正在运行Lollipop。我已经在使用棉花糖设备的setExactAndAllowWhileIdle了。我来看看JobScheduler和GCM。无论如何,我不知道问题出在闹钟响起还是不会响起,还是在闹钟响起时没有唤醒该设备。
塞尔吉奥·维德

0

我认为杀死应用程序不会阻止警报管理器唤醒您的应用程序。

仅当您“强制停止”或禁用该应用程序时,您才不会收到来自警报管理器的回叫。

根本原因可能是其他原因。

同样在M上... setExactAndAllowWhileIdle会进行节流...也就是说,如果您每2分钟安排一次警报,则不会触发该警报。..需要有15分钟的窗口。。


1
感谢您的回答。但是,如果没有,为什么在Smart Manager中禁用“电池优化”后,该应用程序仍能正常运行?
塞尔吉奥·维德斯

您是否在根设备上运行该应用程序。如果是,则应用程序管理器也可以禁用该应用程序
。–

不,我没有在有根设备上运行它。
塞尔吉奥·维德

@rupeshjain“也就是说,如果您每2分钟安排一次警报,它将不会被触发。..需要有15分钟的窗口。” 这不是完全正确的,如果是真的,那就是一个真正的问题。您可以确切地了解在Android文档中进行setExactAndAllowWhileIdle方法进行调度的时间限制。这些警报针对特定应用发出的频率有限制。在正常的系统操作下,在低功率空闲模式下,发出警报的时间不会超过每分钟大约15分钟。
eyadMhanna


0

我们需要在应用程序管理器的自动启动管理器中启用我们的应用程序,某些手机如vivo v5,

在vivo v5中,我们可以在iManager-> App Manager->自动启动管理器中找到此菜单。在此处启用我们的应用。

然后,如果该应用被终止或关闭,您的警报/警报管理器将触发警报。


0

我一直在寻找答案,几个小时后我发现了这一点:

https://stackoverflow.com/a/35220476/3174791

在简历中,是一种了解您的应用是否被“受保护的应用”杀死的方法,并且仅在华为设备上有效。让我知道是否有其他设备(三星,索尼,小米等)的解决方案。


0

这可能已经晚了,但我希望它能对某人有所帮助。

我在同一个问题上停留了很长时间。但是现在我知道如何解决这个问题。这适用于可能有相同问题的任何人。人们一直在说您必须启用自动启动功能,但是我设法使用了自动启动功能。

首先,现在不赞成使用WakeFullBroadcastaReceiver,您应该使用BroadcastReceiver。其次,您必须使用ForegroudService而不是BackgroundService。

我将在下面为您提供示例:

IntentService.class

public class NotificationService extends IntentService {


//In order to send notification when the app is close
//we use a foreground service, background service doesn't do the work.



public NotificationService() {
    super("NotificationService");
}

@Override
public void onCreate() {
    super.onCreate();

}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    //There is no difference in the result between start_sticky or start_not_sticky at the moment
    return START_NOT_STICKY;
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {

    //TODO check if the app is in foreground or not, we can use activity lifecyclecallbacks for this

    startForegroundServiceT();
    sendNotification(intent);
    stopSelf();
}


/***
 * you have to show the notification to the user when running foreground service
 * otherwise it will throw an exception
 */
private void startForegroundServiceT(){

    if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_channel_01";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "Channel human readable title",
                NotificationManager.IMPORTANCE_DEFAULT);

        ((NotificationManager) 
   getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

        Notification notification = new Notification.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();

        startForeground(1, notification);
    }
}

private void sendNotification(Intent intent){

    //Send notification
    //Use notification channle for android O+
}
}

BroadcastReceiver.class中启动前台服务

public class AlarmReceiver extends BroadcastReceiver {


@Override
public void onReceive(Context context, Intent intent) {


    Intent service = new Intent(context, NotificationService.class);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(service);
    } else {
        context.startService(service);
    }

}
}

而setAlarms像这样:

 public static void setAlarm(Context context, int requestCode, int hour, int minute){


    AlarmManager alarmManager =( AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context//same activity should be used when canceling the alarm
            , AlarmReceiver.class);
    intent.setAction("android.intent.action.NOTIFY");

    //setting FLAG_CANCEL_CURRENT makes some problems. and doest allow the cancelAlarm to work properly
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1001, intent, 0);

    Calendar time = getTime(hour, minute);

    //set Alarm for different API levels
    if (Build.VERSION.SDK_INT >= 23){
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pendingIntent);
    }
    else{
        alarmManager.set(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pendingIntent);
    }

然后,您必须在清单中声明接收方和前台服务。

       <receiver android:name=".AlarmReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.NOTIFY">

            </action>
        </intent-filter>
    </receiver>
    <service
        android:name=".NotificationService"
        android:enabled="true"
        android:exported="true"></service>

我希望这可以帮助别人。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.