【问题标题】:Android AlarmManager not working while phone asleep手机睡着时Android AlarmManager不工作
【发布时间】:2016-11-30 20:00:36
【问题描述】:

AlarmManager 有问题。

简而言之,我计划一个alarmManager:

Intent intent = new Intent(context, MyActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delayInMs, pendingIntent);

并且活动MyActivity在指定时间出现。就在设备插入时。当它放在我的口袋里或延迟几分钟时,它也可以工作。 但是当我在晚上之前设置 alarmManager 时,它不会在早上工作。但是,只要我拿起手机或解锁屏幕,它就会起作用。

所以,我想这是由于设备的睡眠模式,但是如何解决这个问题?

1) 我在 myActivity 的每个方法中都添加了一个日志,并且我确信在我手动唤醒设备之前没有人被调用。 2) 我尝试了 PowerManagement 的唤醒锁(在清单中使用 WAKE_LOCK 权限),但没有任何改变:

alarmManager.setExact(.........);
wakeLock = ((PowerManager)contexte.getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "MyActivity");
wakeLock.acquire();

请帮忙!我确定我已经很接近了……

编辑 2016 年 12 月 4 日: 感谢 Nick Friskel 和 Vikram Rao,我将初始代码更改为调用广播接收器并在 onReceive 中获取我的唤醒锁。不幸的是,它似乎不起作用。当电话插上电源或计划在 35 分钟后发出警报时,它可以完美运行,但在整个晚上,onReceive 甚至都没有被调用。 那天晚上我尝试了,计划在上午 9:00 发出警报,但 onReceive 仅在上午 9:46 执行,这意味着我解锁设备的那一刻。 这是我的新代码:

Intent intent = new Intent("com.blah.something.ALARM_RECEIVED");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delayInMs, pendingIntent);

也就是说,由于某种原因,我的日志写“onReceive 的开始”并不是真正在侦听器的开头。我只是设法把它放在真正的开头,所以我会看看是否调用了监听器。

编辑 2016 年 12 月 5 日: 因此,我在 onReceive 顶部更改了日志写入,并且发生了同样的问题:一旦我手动唤醒设备,就会调用 onReceive 的启动。 我可以实现wakefulBroadcastReceiver,但我担心它不会解决任何问题。如果我理解正确,wakefulBroadcastReceiver 有助于防止设备在 onReceive 和活动或服务启动之间休眠。但是如果 onReceive 甚至没有被调用呢? 我有点绝望……也许我应该直接问索尼。 另外,我的手机有耐力模式,但是没有开启。

2016 年 12 月 11 日编辑: 所以,通过更多的测试,我现在确定我什么都不懂......我设置了一个每 5 分钟激活一次的广播接收器(onReceive 在 5 分钟后重置警报管理器),我可以看到它完美地工作......有时.它可以持续几个小时,然后睡两个小时,然后可以睡30分钟,然后再睡。 (当我的手机打开、拔掉电源并闲置时,所有这些)。 我将删除所有代码,但我们感兴趣的是什么。这将更容易理解,我将能够在这里编写所有活动代码。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.par.hasard.mysimpleapplication">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.par.hasard.mysimpleapplication.MySimpleReceiver">
            <intent-filter android:priority="1">
                <action android:name="com.par.hasard.mysimpleapplication.REGULAR_ALARM" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

MainActivity.java

package com.par.hasard.mysimpleapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button)findViewById(R.id.myExportButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MyLogManager.copyLogToClipboard(view.getContext());
                MyLogManager.emptyLogFile(view.getContext());
            }
        });
        try {
            MyLogManager.createLogFile(this);
            MyLogManager.write(this, "Application launched\n");
            MyAlarmPlanner.planAlarm(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MySimpleReceiver.java

package com.par.hasard.mysimpleapplication;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MySimpleReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            MyLogManager.write(context, "Beginning of onReceive\n");
            MyAlarmPlanner.planAlarm(context);
            MyLogManager.write(context, "End of onReceive\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MyAlarmPlanner.java

package com.par.hasard.mysimpleapplication;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import java.io.IOException;

public class MyAlarmPlanner {
    public static void planAlarm(Context context) throws IOException {
        MyLogManager.write(context, "Beginning of alarm planning\n");
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent("com.par.hasard.mysimpleapplication.REGULAR_ALARM");
        PendingIntent pendingIntent =  PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        alarmManager.cancel(pendingIntent);
        alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 300000, pendingIntent);
        MyLogManager.write(context, "End of alarm planning\n");
    }
}

我不认为 MyLogManager.java 有用,它只是无聊的文件管理方法。

长时间空闲后的日志文件内容:

12/12 15h33m23s380 => Beginning of onReceive
12/12 15h33m23s381 => Beginning of alarm planning
12/12 15h33m23s383 => End of alarm planning
12/12 15h33m23s384 => End of onReceive
12/12 15h38m24s337 => Beginning of onReceive
12/12 15h38m24s339 => Beginning of alarm planning
12/12 15h38m24s375 => End of alarm planning
12/12 15h38m24s376 => End of onReceive
12/12 15h43m24s375 => Beginning of onReceive
12/12 15h43m24s376 => Beginning of alarm planning
12/12 15h43m24s380 => End of alarm planning
12/12 15h43m24s381 => End of onReceive
12/12 15h48m25s301 => Beginning of onReceive
12/12 15h48m25s304 => Beginning of alarm planning
12/12 15h48m25s307 => End of alarm planning
12/12 15h48m25s308 => End of onReceive
12/12 15h53m25s316 => Beginning of onReceive
12/12 15h53m25s318 => Beginning of alarm planning
12/12 15h53m25s328 => End of alarm planning
12/12 15h53m25s329 => End of onReceive
12/12 15h58m25s328 => Beginning of onReceive
12/12 15h58m25s329 => Beginning of alarm planning
12/12 15h58m25s331 => End of alarm planning
12/12 15h58m25s333 => End of onReceive
12/12 16h3m26s336 => Beginning of onReceive
12/12 16h3m26s351 => Beginning of alarm planning
12/12 16h3m26s379 => End of alarm planning
12/12 16h3m26s380 => End of onReceive
12/12 16h8m26s397 => Beginning of onReceive
12/12 16h8m26s401 => Beginning of alarm planning
12/12 16h8m26s404 => End of alarm planning
12/12 16h8m26s405 => End of onReceive
12/12 16h13m26s406 => Beginning of onReceive
12/12 16h13m26s407 => Beginning of alarm planning
12/12 16h13m26s410 => End of alarm planning
12/12 16h13m26s411 => End of onReceive
12/12 16h18m27s328 => Beginning of onReceive
12/12 16h18m27s329 => Beginning of alarm planning
12/12 16h18m27s346 => End of alarm planning
12/12 16h18m27s348 => End of onReceive
12/12 16h23m28s298 => Beginning of onReceive
12/12 16h23m28s299 => Beginning of alarm planning
12/12 16h23m28s303 => End of alarm planning
12/12 16h23m28s304 => End of onReceive
12/12 16h28m29s308 => Beginning of onReceive
12/12 16h28m29s310 => Beginning of alarm planning
12/12 16h28m29s323 => End of alarm planning
12/12 16h28m29s324 => End of onReceive
12/12 16h33m29s339 => Beginning of onReceive
12/12 16h33m29s340 => Beginning of alarm planning
12/12 16h33m29s355 => End of alarm planning
12/12 16h33m29s361 => End of onReceive
12/12 16h38m29s356 => Beginning of onReceive
12/12 16h38m29s357 => Beginning of alarm planning
12/12 16h38m29s360 => End of alarm planning
12/12 16h38m29s361 => End of onReceive
12/12 16h43m29s364 => Beginning of onReceive
12/12 16h43m29s365 => Beginning of alarm planning
12/12 16h43m29s367 => End of alarm planning
12/12 16h43m29s369 => End of onReceive
12/12 16h48m29s376 => Beginning of onReceive
12/12 16h48m29s380 => Beginning of alarm planning
12/12 16h48m29s390 => End of alarm planning
12/12 16h48m29s394 => End of onReceive
12/12 16h53m29s392 => Beginning of onReceive
12/12 16h53m29s394 => Beginning of alarm planning
12/12 16h53m29s402 => End of alarm planning
12/12 16h53m29s403 => End of onReceive
12/12 17h43m33s986 => Beginning of onReceive      //problem, the 16'58 onReceive wasn't called
12/12 17h43m33s988 => Beginning of alarm planning
12/12 17h43m33s996 => End of alarm planning
12/12 17h43m34s4 => End of onReceive
12/12 17h48m34s535 => Beginning of onReceive
12/12 17h48m34s536 => Beginning of alarm planning
12/12 17h48m34s539 => End of alarm planning
12/12 17h48m34s540 => End of onReceive
12/12 18h29m49s635 => Beginning of onReceive     //the moment I turned on my device
12/12 18h29m49s648 => Beginning of alarm planning
12/12 18h29m49s667 => End of alarm planning
12/12 18h29m49s668 => End of onReceive

谁能告诉我我的错误在哪里?

【问题讨论】:

  • 哪个安卓版本和手机是什么牌子的?根据我的经验,不同版本的 android 的行为略有不同。一些由制造商定制的手机在睡眠/唤醒相关操作方面的行为也有所不同,主要是为了节省电池,但会限制其他功能。
  • 这是一台索尼 XPERIA E5。是的,我知道这一点,但我希望,如果我可以在我的设备上运行它,那么在大多数其他设备上都可以。我希望...

标签: android alarmmanager sony wakelock sleep-mode


【解决方案1】:

感谢 CommonsWare,问题解决了!此失败是由于打盹模式 (https://developer.android.com/training/monitoring-device-state/doze-standby.html) 简而言之,从 Android 6.0 开始,如果设备处于打盹模式,AlarmManager 会受到影响并且无法触发。但是您可以将 setExact 替换为 setExactAndAllowWhileIdle。有限制,但我们必须处理。 CommonsWare 回答的帖子有链接: sendWakefulWork not always called with cwac-wakeful-1.1.0

【讨论】:

    【解决方案2】:

    Android 中的AlarmManager api 有其局限性。对于 -

    1. 在设备重启时清除(所有警报丢失)
    2. 在设备锁定/睡眠状态期间,制造商和 android 版本的行为不一致

    我解决这些问题的方法是 -

    1. 创建具有广播意图的警报,然后将侦听器添加到该广播以执行必要的操作。

    像这样-

    Intent intent = new Intent("com.blah.something.ALARM_RECIEVED");
    PendingIntent pendingIntent =  PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_ONE_SHOT);
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Html5Activity.ALARM_SERVICE);
    alarmManager.set(AlarmManager.RTC, triggerTimeInMillis, pendingIntent);
    

    清单文件 -

    <receiver android:name=".receiver.BackgroundScheduledAlarmReceiver">
         <intent-filter android:priority="1">
              <action android:name="com.blah.something.ALARM_RECIEVED" />
         </intent-filter>
    </receiver>
    
    1. 将警报保存在 sqlite 或其他地方(此处未显示)并在设备重启时通过像这样监听设备启动来重新创建它们 -

    清单文件-

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    ...
    <receiver
        android:name=".receiver.RecreateAlarmsAtBootReceiver"
        android:enabled="true"
        android:exported="true"
        android:label="RecreateAlarmsAtBootReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        </intent-filter>
    </receiver>
    

    RecreateAlarmsAtBootReceiver中,读取存储告警的sqlite并再次添加到告警管理器中。

    【讨论】:

    • 第二点,我已经添加了一个RECEIVE_BOOT_COMPLETED,它使用保存的数据初始化警报。它似乎工作,虽然我没有花很多时间来测试它。对于第一点,我会尝试,并让您与结果取得联系
    • broadcastReceiver暂时没有解决问题,设备拔出长时间不活动时似乎没有调用监听器。正如我在编辑过的问题中所说,我会确保这一点。
    【解决方案3】:

    执行此操作的正确方法是让您的 AlarmManager 触发 BroadcastReceiver 而不是直接的 Activity。然后,您可以将唤醒锁放在广播接收器类中,然后从 BroadcastReceiver 或从 BroadcastReceiver 启动的 IntentService 运行您的活动。

    你的意图会变成这样:

    Intent intent = new Intent(context, MyActivityReceiver.class);
    

    然后制作你的广播接收器:

    package com.yourpackage (change this to your package)
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    
    public class MyActivityReceiver extends BroadcastReceiver {
        public static final int REQUEST_CODE = 0; //only necessary if you have more receivers 
    
        // when the alarm is triggered
        @Override
        public void onReceive(Context context, Intent intent) {
        wakeLock = ((PowerManager)contexte.getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "MyActivity");
        wakeLock.acquire();
        Intent i = new Intent(context, MyActivity.class);
        context.startService(i);
        }
    }
    

    并将接收者添加到您的清单中:

    <receiver
                android:name="service.TimeService"
                android:enabled="true"
                android:exported="false"
                >
    </receiver>
    

    如果这不起作用,那么您需要通过 WakefulService 开始您的活动。

    【讨论】:

    • 感谢您的回答。我刚刚编辑了我的问题。所以我会确认我的 onReceive 没有被调用,如果得到确认,我会试试你的 WakefulService 想法。
    • 所以只有在手机插入时才调用onReceive?
    • 不,onReceive 在手机插入或空闲几分钟时完美调用。就是拔掉电源闲置了很长时间才不会被调用。
    猜你喜欢
    • 2016-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多