Android에 대한 모든 최신 제한 사항 이후에 정확한 시간에 알람이 예약되도록 설정하는 방법은 무엇입니까?


27

참고 : 여기에 대한 다양한 솔루션을 시도했습니다 (예 : here ). 찾은 솔루션이 아래에서 작성한 테스트를 사용하여 작동하는지 확인하지 않고 닫지 마십시오.

배경

앱에는 사용자가 미리 알림을 특정 시간에 예약하도록 요구해야 하므로이 시간에 앱이 트리거 될 때 백그라운드에서 작은 작업 (일부 DB 쿼리 작업)을 수행하고 알림에 대한 간단한 알림.

과거에는 간단한 코드를 사용하여 상대적으로 특정 시간에 예약 할 항목을 설정했습니다.

            val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
            when {
                VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
                else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
            }
class AlarmReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("AppLog", "AlarmReceiver onReceive")
        //do something in the real app
    }
}

용법:

            val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
            setAlarm(this, timeToTrigger, 1)

문제

이제 새로운 Android 버전 및 Android 10의 Pixel 4에서 에뮬레이터 에서이 코드를 테스트했으며 트리거 한 것처럼 보이지 않거나 제공 한 이후로 오랜 시간 후에 트리거 될 수 있습니다. 일부 OEM 이 최근 작업에서 앱을 제거하기 위해 추가 한 끔찍한 동작 을 잘 알고 있지만 에뮬레이터와 Pixel 4 장치 (스톡)에 있습니다.

알람 설정 에 대한 문서를 읽었 으므로 앱이 너무 자주 발생하지 않도록 앱에 제한되어 있지만 특정 시간에 알람을 설정하는 방법은 설명하지 않으며 설명하지 않습니다. 어떻게 온 구글의 시계 응용 프로그램이 그 일을 성공합니다.

그뿐만 아니라 내가 이해 한 바에 따르면, 특히 장치의 저전력 상태에 대해 제한 사항을 적용해야한다고 말하지만 제 경우에는 장치와 에뮬레이터 모두 에서이 상태가 없었습니다. 지금부터 약 1 분 안에 알람이 트리거되도록 설정했습니다.

많은 알람 시계 앱이 더 이상 작동하지 않는 것을 확인하면 문서에 누락 된 것이 있다고 생각합니다. 이러한 앱의 예로는 Google 에서 구입 했지만 새로운 제한 사항을 처리하기 위해 새로운 업데이트를받지 못한 인기있는 Timely 앱이 있으며, 이제 사용자 는 다시 원합니다. . 그러나 일부 인기있는 애플 리케이션과 같은, 잘 작동을 이것 .

내가 시도한 것

실제로 알람이 작동하는지 테스트하기 위해 처음으로 앱을 설치 한 후 장치가 PC에 연결되어있는 동안 (로그를 보려면) 알람을 트리거하려고 할 때 이러한 테스트를 수행합니다.

  1. 앱이 포 그라운드에 있고 사용자에게 표시 될 때 테스트합니다. -1-2 분 걸렸습니다.
  2. 앱이 백그라운드로 전송 된시기 테스트 (예 : 홈 버튼 사용)-약 1 분 소요
  3. 앱의 작업이 최근 작업에서 제거 된시기를 테스트합니다. -20 분 이상 기다렸는데 로그에 기록하면서 알람이 트리거되는 것을 보지 못했습니다.
  4. # 3처럼 화면을 끕니다. 아마 더 나쁠 것입니다 ...

나는 다음 것들을 사용하려고했지만 모두 작동하지 않습니다.

  1. alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)

  2. alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)

  3. AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)

  4. 위와 함께 다음과의 조합 :

    if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)

  5. BroadcastReceiver 대신 서비스를 사용하려고했습니다. 다른 프로세스에서도 시도했습니다.

  6. 배터리 최적화에서 앱을 무시하려고 시도했지만 (도움이되지 않음) 다른 앱은 필요하지 않으므로 사용하지 않아야합니다.

  7. 이것을 사용하여 시도 :

            if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
                alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
            AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
  1. onTaskRemoved 트리거가있는 서비스를 시도 하여 알람을 다시 예약했지만 도움이되지 않았습니다 (서비스는 정상적으로 작동했습니다).

Google 시계 앱의 경우 트리거되기 전에 알림을 표시하는 것을 제외하고는 배터리 최적화 설정 화면의 "최적화되지 않은"섹션에도 표시되지 않습니다.

이것이 버그처럼 보이는 것을 보았으므로 문제를 보여주는 샘플 프로젝트 및 비디오를 포함하여 여기 에 대해보고 했습니다.

여러 버전의 에뮬레이터를 확인 했는데이 동작은 API 27 (Android 8.1-Oreo)에서 시작된 것으로 보입니다. 상대 워드 프로세서에서 , 나는 알람 관리기가 언급되고, 대신에, 그것은 다양한 배경의 작품에 대해 기록 된 표시되지 않습니다.

질문

  1. 요즘 비교적 정확한 시간에 트리거되도록 무언가를 어떻게 설정합니까?

  2. 위의 솔루션이 더 이상 작동하지 않습니까? 아무것도 빠졌습니까? 허가? 어쩌면 작업자를 대신 사용해야합니까? 그러나 그것이 제 시간에 전혀 트리거되지 않을 수도 있다는 것을 의미하지 않습니까?

  3. Google "시계"앱은이 모든 것을 어떻게 극복하고 1 분 전에 트리거 된 경우에도 항상 정확한 시간에 트리거합니까? 시스템 앱이기 때문입니까? 앱이 내장되어 있지 않은 기기에 사용자 앱으로 설치되면 어떻게 되나요?

시스템 응용 프로그램이기 때문에 2 분 안에 두 번 경보를 트리거 할 수있는 다른 응용 프로그램을 찾았습니다. 여기 에서는 전경 서비스를 사용할 수 있다고 생각합니다.

편집 : 여기에 아이디어를 시도하기 위해 작은 Github 저장소를 만들었습니다 .


편집 : 마침내 공개 소스 이며이 문제가없는 샘플발견했습니다 . 슬프게도 매우 복잡하며 여전히 최신 작업에서 앱을 제거한 후에도 알람이 예약 된 상태를 유지할 수 있도록 다른 점과 POC에 추가 해야하는 최소 코드는 무엇인지 파악하려고합니다.


그것은 오랜 시간 동안 서비스 (내가 제안하는 프로 개발자는 아닙니다)에서 일했지만 시간 서비스가 실행 된 후 안드로이드 제한으로 인해 알람을 5 분 미만으로 설정하는 것과 같은 경우 alarmManager를 피할 것을 제안 할 수 있습니다. 백엔드마다 5 분 또는 5 분 이상 후에 호출됩니다. 대신 Handler를 사용했습니다. 그리고 내 서비스를 계속 실행하기 위해 내가 언급 한 배경 [ github.com/fabcira/neverEndingAndroidService]
Blu

정확한 제한은 무엇입니까? 트리거가 비교적 정확한 시간을주기 위해 작동하는 최소 시간은 얼마입니까?
안드로이드 개발자

정확한 제한 사항을 기억할 수는 없지만 그 일을 할 때 백그라운드 서비스가 자동으로 종료되는 것을 극복하기 위해 며칠 동안 봤습니다. 개인적으로 볼 때 Samsung, Xiaomi 등의 문제를 발견했으며 5 분 간격으로 alarmManger를 호출 할 수 없었습니다 .1 분마다 트리거되는 alarmManger를 사용하여 데이터 업로드 서비스를 구현했지만 서비스를 불평 한 고객에게 실망했습니다. 전혀 실행되지 않습니다. 에뮬레이터의 경우 잘 작동합니다.
Blu

Android Q의 백그라운드에서 활동을 시작할 수는 없지만 귀하의 경우와 같지 않습니다.
marcinj

@ greeble31 지금 시도했습니다. 어떤 솔루션이 효과가 있습니까? 어떤 이유로 든 여전히 작동하지 않습니다. 알람을 설정하고 최근 작업에서 앱을 제거했는데 화면이 켜져 있고 장치가 충전기에 연결되어 있어도 알람이 트리거되는 것을 볼 수 없습니다. 실제 장치 (Android 10이 설치된 Pixel 4)와 에뮬레이터 (예 : API 27) 모두에서 발생합니다. 그것은 당신을 위해 작동합니까? 전체 코드를 공유 할 수 있습니까? 아마도 Github에 있습니까?
안드로이드 개발자

답변:


4

우리는 할 일이 없습니다.

앱이 허용 목록에 없으면 최근 앱에서 제거한 후에는 항상 종료됩니다.

때문에 원래 장비 제조업체 (OMES)는 지속적으로 안드로이드 준수를 위반 .

따라서 앱이 기기 제조에서 허용 목록에없는 경우 앱이 최근 앱에서 제거 된 경우 에도 알람이 발생하더라도 백그라운드 작업이 실행되지 않습니다 .

여기 에서 해당 동작을 가진 장치 목록을 찾을 수 있습니다. 또한 부작용을 찾을 수도 있지만 제대로 작동하지 않습니다.


나는이 중국 OEM 문제를 잘 알고있다. 그러나 내가 쓴 것처럼 에뮬레이터 및 Pixel 4 장치에서도 발생합니다. 그런 중국 OEM이 아닙니다. 에뮬레이터 및 / 또는 픽셀 장치를 확인하십시오. 이 문제도 존재합니다. 알람을 설정하고 최근 작업에서 앱을 제거하고 알람이 트리거되지 않는지 확인하십시오. 버그로보고 여기에보고합니다 (원하는 경우 비디오 및 샘플 프로젝트 포함) : issuetracker.google.com/issues/149556385 명확하게하기 위해 내 질문을 업데이트했습니다. 질문은 어떻게 일부 앱이 성공했는지입니다.
안드로이드 개발자

@androiddeveloper 에뮬레이터에서 작동해야한다고 생각합니다. 어떤 에뮬레이터가 있습니까?
이브라힘 알리

나는 또한 시도 할 때까지 믿었다. 예를 들어 Android Studio가 제공하는 것의 API 29에서 시도하십시오. 조금 오래된 버전에서도 같은 일이 일어날 것이라고 확신합니다.
안드로이드 개발자

4

Android R을 포함한 모든 버전에서 작동 하는 이상한 해결 방법을 찾았습니다 ( here here ).

  1. 매니페스트에 SAW 권한을 선언하십시오.
      <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Android R에서는 부여해야합니다. 전에는 당연히 선언 해야하는 것처럼 보일 필요가 없습니다. 확실하지이 R에 변경,하지만 난 작성된 것을보고는, 백그라운드에서 일을 시작하기 위해 가능한 솔루션으로 요구 될 수 말할 수있는 이유는 여기에 안드로이드 10.

  1. 작업이 제거 된시기를 감지 할 수있는 서비스를 보유하고있는 경우이를 수행 할 때 가짜 활동을여십시오.
class OnTaskRemovedDetectorService : Service() {
    override fun onBind(intent: Intent?) = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY

    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
        Log.e("AppLog", "onTaskRemoved")
        applicationContext.startActivity(Intent(this, FakeActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
        stopSelf()
    }

}

FakeActivity.kt

class FakeActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("AppLog", "FakeActivity")
        finish()
    }
}

이 테마를 사용하여이 활동을 사용자에게 거의 보이지 않게 할 수도 있습니다.

    <style name="AppTheme.Translucent" parent="@style/Theme.AppCompat.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>

슬프게도, 이것은 이상한 해결 방법입니다. 이에 대한 더 좋은 해결 방법을 찾고 싶습니다.

제한 사항은 활동 시작에 대해 이야기하므로 현재 아이디어는 초 단위로 포 그라운드 서비스를 시작하면 도움이 될 것이며 SAW 권한이 필요하지 않을 수도 있습니다.

편집 : 확인 전경 서비스 (샘플 여기 )를 시도했지만 작동하지 않았습니다. 활동이 작동하지만 서비스가 아닌 이유를 모릅니다. 나는 심지어 거기에서 알람을 다시 예약하려고 시도하고 다시 예약 한 후에도 약간의 서비스를 유지하려고했습니다. 또한 정상적인 서비스를 시도했지만 물론 작업이 제거되어 즉시 닫히고 전혀 작동하지 않았습니다 (배경에서 실행할 스레드를 만든 경우에도).

내가 시도하지 않은 또 다른 가능한 해결책은 포 그라운드 서비스를 영원히 또는 적어도 작업이 제거 될 때까지하는 것입니다.

편집 : 앱의 작업을 제거하기 전에 포 그라운드 서비스를 실행하려고 시도한 후 잠시 후에 알람이 계속 작동했습니다. 또한이 서비스를 작업 제거 이벤트를 담당하고 이벤트가 발생할 때 즉시 닫히려고했지만 여전히 작동했습니다 (샘플 여기 ). 이 대안의 장점은 SAW 권한이 전혀 필요하지 않다는 것입니다. 단점은 앱이 이미 사용자에게 표시되는 동안 알림이있는 서비스가 있다는 것입니다. 앱이 이미 활동을 통해 포 그라운드에있는 동안 알림을 숨길 수 있는지 궁금합니다.


편집 : Android Studio의 버그 인 것으로 보입니다 ( 버전 비교 비디오를 포함하여 여기 에 보고 됨 ). 시도한 문제가있는 버전에서 앱을 시작하면 알람이 해제 될 수 있습니다.

런처에서 앱을 시작하면 제대로 작동합니다.

알람을 설정하는 현재 코드입니다.

        val timeToTrigger = System.currentTimeMillis() + 10 * 1000
        val pendingShowList = PendingIntent.getActivity(this, 1, Intent(this, SomeActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
        val pendingIntent = PendingIntent.getBroadcast(this, 1, Intent(this, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
        manager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingShowList), pendingIntent)

"pendingShowList"를 사용할 필요조차 없습니다. null을 사용하는 것도 좋습니다.


AndroidQ에서 onReceive () 활동을 원합니다. SYSTEM_ALERT_WINDOW허가 없이 그에 대한 해결 방법이 있습니까?
doctorram

2
Google은 왜 항상 안드로이드 개발자의 삶을 단순한 것들보다 생생하게 만들어 줍니까?!
doctorram

@doctorram 네, 다양한 예외에 대해 문서에 작성되었습니다 : developer.android.com/guide/components/activities/… . 테스트하기 가장 쉬운 SYSTEM_ALERT_WINDOW를 선택했습니다.
안드로이드 개발자

마지막 편집에서 최근 목록에서 앱을 제거한 후 알람을 유지하기 위해 언급 한 해결 방법을 사용할 필요가 없다는 것을 의미합니까 ??
user3410835

앱이 최근 목록에서 제거 된 경우에도 매일 오전 6시에서 오전 7시 사이에 백그라운드에서 코드 조각을 실행하고 싶습니다. WorkManager 또는 AlarmManager를 사용해야합니까 ?? 내 유스 케이스에 대해 다음 코드를 시도했지만 작동하지 않았습니다. 아래 코드의 문제점은 무엇입니까? calendar.setTimeInMillis (System.currentTimeMillis ()); calendar.set (캘린더 .HOUR_OF_DAY, 6); alarmManager.setInexactRepeating (AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis (), AlarmManager.INTERVAL_DAY, pendingIntent);
user3410835

1
  1. 브로드 캐스트하려는 의도가 명시적이고 Intent.FLAG_RECEIVER_FOREGROUND플래그 가 있는지 확인하십시오 .

https://developer.android.com/about/versions/oreo/background#broadcasts

Intent intent = new Intent(context, Receiver.class);
intent.setAction(action);
...
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

PendingIntent operation = PendingIntent.getBroadcast(context, 0, intent, flags);
  1. setExactAndAllowWhileIdle()API 23 이상을 타겟팅 할 때 사용하십시오 .
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, operation);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, operation);
} else {
    alarmManager.set(AlarmManager.RTC_WAKEUP, time, operation);
}
  1. 포 그라운드 서비스로 알람을 시작하십시오.

https://developer.android.com/about/versions/oreo/background#migration

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(intent);
} else {
    context.startService(intent);
}
  1. 그리고 권한을 잊지 마십시오 :
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

BroadcastReceiver 자체가 인 텐트를 전혀 얻지 못하는 경우 (또는 가까운 시간에) 서비스에 왜 중요한가? 이것이 첫 번째 단계입니다 ... 또한 AlarmManagerCompat이 이미 동일한 코드를 제공하지 않습니까? 최근 작업에서 앱을 제거하는 것을 포함하여 내가 작성한 테스트를 사용하여 이것을 시도 했습니까? 전체 코드를 보여줄 수 있습니까? Github에서 공유 할 수 있습니까?
안드로이드 개발자

@androiddeveloper가 답변을 업데이트했습니다.
막심 이바노프

여전히 작동하지 않는 것 같습니다. 다음은 샘플 프로젝트입니다 : ufile.io/6qrsor7o . Android 10 (에뮬레이터도 괜찮음)에서 알람을 설정하고 최근 작업에서 앱을 제거하십시오. 최근 작업에서 제거하지 않으면 제대로 작동하고 10 초 후에 트리거됩니다.
안드로이드 개발자

또한 샘플 프로젝트 및 비디오가 포함 된 버그 보고서에 대한 링크를 갖도록 질문을 업데이트했습니다. 왜냐하면 이것이 발생하는 다른 이유가 없기 때문에 이것이 버그라고 생각하기 때문입니다.
안드로이드 개발자

0

나는 이것이 효율적이지 않지만 60 초의 정확도와 더 일관성이있을 수 있음을 알고 있습니다.

https://developer.android.com/reference/android/content/Intent#ACTION_TIME_TICK

이 브로드 캐스트 수신기가 포 그라운드 서비스 내에서 사용되는 경우 1 분마다 시간을 확인하고 조치를 취할 수 있습니다.


포 그라운드 서비스가있는 경우 왜 필요한가요? 원하는 경우 Handler.postDelayed 또는 다른 유사한 솔루션을 사용할 수 있습니다.
Android 개발자

0

에너지 절약 모드를 비활성화하도록 사용자에게 권한을 설정하도록 요청할 수 있으며, 사용하지 않으면 정확한 시간을 얻지 못할 것이라고 경고합니다.

요청 코드는 다음과 같습니다.

PowerManager powerManager = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
            String packageName = "your Package name";
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                Intent i = new Intent();
                if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
                    i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                    i.setData(Uri.parse("package:" + packageName));
                    startActivity(i);

다른 응용 프로그램이없는 것으로 나타 났으며 도움이되는지 궁금한 것으로 이미 알았으므로 이미 시도했습니다. 작동하지 않았다. 질문이 업데이트되었습니다.
안드로이드 개발자

0

나는 당신이 당신의 질문에 언급 한 오픈 소스 프로젝트의 저자입니다 ( 간단한 알람 시계) .

내 앱이 정확히 그렇게하기 때문에 AlarmManager.setAlarmClock 사용이 효과가 없다는 것에 놀랐습니다. 코드는 AlarmSetter.kt 파일에 있습니다. 다음은 스 니펫입니다.

  val pendingAlarm = Intent(ACTION_FIRED)
                .apply {
                    setClass(mContext, AlarmsReceiver::class.java)
                    putExtra(EXTRA_ID, id)
                    putExtra(EXTRA_TYPE, typeName)
                }
                .let { PendingIntent.getBroadcast(mContext, pendingAlarmRequestCode, it, PendingIntent.FLAG_UPDATE_CURRENT) }

            val pendingShowList = PendingIntent.getActivity(
                    mContext,
                    100500,
                    Intent(mContext, AlarmsListActivity::class.java),
                    PendingIntent.FLAG_UPDATE_CURRENT
            )

            am.setAlarmClock(AlarmManager.AlarmClockInfo(calendar.timeInMillis, pendingShowList), pendingAlarm)

기본적으로 그것은 특별한 것이 아닙니다. 인 텐트에는 액션과 대상 클래스가 있는지 확인하십시오.이 경우 내 방송 수신기입니다.


슬프게도 효과가 없었습니다. 그것이 제가 시도한 것입니다. 여기 파일을 참조하십시오 : github.com/yuriykulikov/AlarmClock/issues/…
안드로이드 개발자

GitHub에서 코드를 확인했습니다. Moto Z2 Play의 앱에서 최근에 앱을 제거한 후에 브로드 캐스트 리시버가 작동합니다. Pixel에서 시도해 볼 수는 있지만 코드는 괜찮습니다. 애플리케이션을 강제 종료하면 예약 된 알람이 제거되지만 강제 종료 된 모든 앱에서 발생합니다.
유리 Kulikov

이미 여러 번 표시했습니다. 예약 후 내가하는 일은 최근 작업에서 제거하는 것입니다. 그리고 에뮬레이터와 Pixel 4에서 모두했습니다.
Android 개발자

pendingShowList를 사용하여 문제를 피할 수 있는지 확인하고 그렇다면 대답을 업데이트하겠습니다. 아마도 누군가에게 유용 할 것입니다.
유리 Kulikov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.