Android AlarmManager не работает во время сна телефона

у меня проблема с 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();

пожалуйста, помогите ! Я уверена, что я так близко...

редактировать Dec 04'16 : Благодаря Нику Фрискелу и Викраму РАО, я изменил свой первоначальный код, чтобы вызвать broadcastReceiver и получить свой wakeLock в в 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" не в начале слушатель почему-то. Мне просто удалось поставить его в реальное начало, поэтому я посмотрю, вызван ли слушатель или нет.

редактировать Dec 05'16 : Итак, я изменил запись журнала в верхней части onReceive, и произошла та же проблема : запуск onReceive вызывается, как только я вручную разбужу устройство. Я могу реализовать wakefulBroadcastReceiver, но я боюсь, что это ничего не решит. Если я правильно понял, wakefulBroadcastReceiver полезен для предотвращения спящий режим между onReceive и запуском действия или службы. Но что, если onReceive даже не позвонил ? Я в отчаянии... Может быть, мне стоит напрямую спросить Sony. Кроме того, мой телефон имеет режим выносливости, но он не активирован.

EDIT Dec 11'16: Итак, с новыми тестами я теперь уверен, что ничего не понимаю.... Я установил broadcastReceiver, который активируется каждые 5 минут (onReceive сбрасывает alarmManager через 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 launchedn");
            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 onReceiven");
            MyAlarmPlanner.planAlarm(context);
            MyLogManager.write(context, "End of onReceiven");
        } 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 planningn");
        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 planningn");
    }
}

Я не думаю, что 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

может кто-нибудь сказать мне, где моя ошибка ?

3 ответов


благодаря CommonsWare, проблема решена ! Это не из-за Doze режим (https://developer.android.com/training/monitoring-device-state/doze-standby.html) Короче говоря, начиная с Android 6.0, AlarmManager влияет и не может работать, если устройство находится в этом режиме дремоты. Но вы можете заменить setExact на setExactAndAllowWhileIdle. Есть ограничения, но мы должны иметь с ними дело. Есть ссылка на сообщение, где CommonsWare ответил : sendWakefulWork не всегда называется с cwac-wakeful-1.1.0


AlarmManager api в android имеет свои ограничения. Ибо -

  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 хранения сигналов тревоги и добавляет их в Alarm manager снова.


правильным способом сделать это было бы, чтобы ваш AlarmManager запускал BroadcastReceiver вместо прямого действия. Затем вы поместите свой wakelock внутри класса широковещательного приемника, а затем запустите свою деятельность из BroadcastReceiver или из IntentService, который запускается из BroadcastReceiver.

ваше намерение будет изменено на следующее:

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.