Androidのすべての最新の制限の後、正確な時間にスケジュールされるようにアラームを設定するにはどうすればよいですか?


27

注:StackOverflowでここに記述されているさまざまなソリューションを試しました(ここの例)。あなたが見つけたものからのあなたの解決策が私が以下に書いたテストを使用して機能するかどうかをチェックせずにこれを閉じないでください。

バックグラウンド

アプリには、ユーザーがリマインダーを特定の時間にスケジュールするように設定するという要件があります。そのため、この時間にアプリがトリガーされると、バックグラウンドで小さな処理が行われ(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は、最近のタスクからアプリケーションを削除するに追加しましたが、この1はエミュレータとピクセル4デバイス(株)の両方です。

アラームの設定に関するドキュメントを読みましが、アプリで制限されているため、あまり発生しませんが、これは特定の時間にアラームを設定する方法を説明しておらず、説明もしていませんどのように来るGoogleの時計アプリはそれをやって成功しました。

それだけでなく、私が理解しているところによると、制限は特にデバイスの低電力状態に適用する必要があると述べていますが、私の場合、デバイスとエミュレーターの両方でこの状態がありませんでした。約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)から始まったようです。docsを見ると、 AlarmManagerが言及されていないように見えますが、代わりに、さまざまなバックグラウンド作業について書かれています。

質問

  1. 現在、比較的正確な時間にトリガーされるように何かを設定するにはどうすればよいですか?

  2. 上記の解決策が機能しなくなったのはなぜですか?何か不足していますか?許可?代わりにワーカーを使用することになっているのでしょうか?しかし、それはそれが時間どおりにトリガーされない可能性があることを意味しませんか?

  3. Googleの「時計」アプリは、これらすべてをどのように克服し、たとえちょうど1分前にトリガーされたとしても、常に正確な時間にトリガーしますか?それはシステムアプリだからというだけのことですか?それが組み込まれていないデバイスに、ユーザーアプリとしてインストールされた場合はどうなりますか?

システムアプリだからといって、ここで2分間に2回アラームをトリガーできるアプリをもう1つ見つけまし。ただし、フォアグラウンドサービスを使用することもあると思います。

編集:ここで、アイデアを試すための小さなGithubリポジトリを作成しました。


編集:ようやくオープンソースであり、この問題のないサンプル見つかりました。悲しいことに、それは非常に複雑であり、私はまだそれが何がそんなに違うのか(そして私が私のPOCに追加する必要がある最小限のコードは何ですか)を理解しようとします


それは私がサービスに取り組んだ長い時間です(私はプロの開発者でさえ提案していません)が、アラームを5分未満に設定するような場合は、alarmManagerを回避することを提案できます。 5分以上5分以上後に呼び出されるバックエンド。代わりに、ハンドラーを使用しました。そして、サービスを実行するために、私が参照したバックグラウンドで続行します[ github.com/fabcira/neverEndingAndroidService]
Blu

正確な制限は何ですか?トリガーが比較的正確な時間で動作することが保証されている最小時間はどれくらいですか?
Android開発者

正確な制限を思い出せませんが、それに取り組んでいたときに、バックグラウンドサービスが自動的に終了するのを克服するために、何日もグーグル検索しました。個人的な観察から、Samsung、Xiaomiなどの問題に気付いたので、5分間隔でalarmMangerを呼び出すことができません、1分ごとにトリガーされるalarmMangerを使用してデータアップロードサービスを実装しましたが、サービスに不満を言ったクライアントを失望させましたまったく実行されていません。エミュレーターの場合、これはうまく機能します。
Blu

Android Qではバックグラウンドからアクティビティを開始できないことを知っていますが、それはあなたのケースのようには見えません。
marcinj

@ greeble31試しました。どのソリューションが機能していると思いますか?どういうわけか私はまだそれを働かせません。アラームを設定し、最近のタスクからアプリを削除しました。画面がオンになっていて、デバイスが充電器に接続されているのに、アラームがトリガーされていません。実際のデバイス(Android 10を搭載したPixel 4)とエミュレーター(API 27など)の両方で発生します。それはあなたのために働きますか?完全なコードを共有していただけますか?たぶんGithubに?
Android開発者

回答:


4

何もする必要はありません。

アプリがホワイトリストに登録されない場合、最近のアプリから削除されると、常に強制終了されます。

のでオリジナル機器メーカー(OMEs)は常にAndroidの遵守に違反します

そのため、アプリがデバイスManufactureからホワイトリストに登録されていない場合、アプリは最近のアプリから削除された場合でも、アラームでさえバックグラウンドで動作しません。

あなたはその行動を持つデバイスのリストを見つけることができるここにしかし、それはうまく動作しません、また、あなたがサイド解決策を見つけるかもしれません。


私はこの中国のOEMの問題をよく知っています。しかし、私が書いたように、それはエミュレーターとPixel 4デバイスでも起こります。そのようにしたのは、一部の中国のOEMではありません。エミュレーターやPixelデバイスで確認してください。問題はそこにも存在します。アラームを設定し、最近のタスクからアプリを削除して、アラームがトリガーされていないことを確認します。私はこれをバグと見なして、ここで報告します(試してみたい場合は、ビデオとサンプルプロジェクトが含まれています):issuetracker.google.com/issues/149556385。明確にするために質問を更新しました。問題は、なぜいくつかのアプリが成功したかです。
Android開発者

@androiddeveloperエミュレーターで動作するはずだと思います。
イブラヒムアリ

私が試すまで、私も信じていました。たとえば、Android Studioが提供するAPI 29で試してみてください。同じことが少し古いバージョンでも起こると確信しています。
Android開発者

4

Android Rを含むすべてのバージョンで機能するように思われる奇妙な回避策(ここのサンプル)を見つけました。

  1. マニフェストで宣言されたSAW権限を許可します。
      <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Android Rでは、それも許可する必要があります。以前は、付与する必要があるとは思われず、宣言しただけです。これがRで変更された理由はわかりませんが、ここでAndroid 10に記述されているように、バックグラウンドで物事を開始するための可能なソリューションとしてSAWが必要になる可能性があると言えます。

  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>

悲しいことに、これは奇妙な回避策です。これに対するより良い回避策を見つけたいと思います。

制限はActivityの開始について話しているので、私の現在の考えは、フォアグラウンドサービスを1秒間起動した場合にも役立つだろうということです。これには、SAW権限も必要ありません。

編集:OK私はフォアグラウンドサービス(ここのサンプル)で試しましたが、うまくいきませんでした。アクティビティが機能しているのにサービスではない理由はわかりません。私はそこでアラームを再スケジュールしようとし、再スケジュールした後でもサービスを少しの間とどまらせようとしました。通常のサービスも試しましたが、タスクが削除されたため、もちろんすぐに閉じてしまい、まったく機能しませんでした(バックグラウンドで実行するスレッドを作成した場合でも)。

私が試していない別の可能な解決策は、フォアグラウンドサービスを永久に使用するか、少なくともタスクが削除されるまでですが、これは少し奇妙で、私が言及したアプリがそれを使用して表示されません。

編集:アプリのタスクを削除する前とその後少しの間、フォアグラウンドサービスを実行しようとしましたが、アラームは引き続き機能しました。また、このサービスがタスク削除イベントを担当するようにして、発生したときにすぐにサービスを閉じるように試みましたが、それでも機能しました(ここのサンプル)。この回避策の利点は、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は常にAndroid開発者の生活を単純なものよりも地獄にしているのですか?
doctorram

@doctorramはい、さまざまな例外に関するドキュメントに記述されています:developer.android.com/guide/components/activities/… 。テストが最も簡単なため、SYSTEM_ALERT_WINDOWを選択しました。
Android開発者

最後の編集から、アプリを最近のリストから削除した後にアラームを永続化するために、あなたが言及した回避策を使用する必要がないことを意味しますか?
user3410835

アプリが最近のリストから削除されている場合でも、毎日午前6時から午前7時までバックグラウンドでコードを実行したい。WorkManagerまたはAlarmManagerを使用する必要がありますか?私のユースケースで次のコードを試してみましたが、機能しませんでした。以下のコードの問題は何ですか?calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.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で共有しますか?
Android開発者

@androiddeveloperが回答を更新しました。
マクシムイワノフ

それでも動作しないようです。これがサンプルプロジェクトです:ufile.io/6qrsor7o。Android 10(エミュレータもOK)で試して、アラームを設定し、最近のタスクからアプリを削除してください。最近のタスクから削除しないと、正常に動作し、10秒後にトリガーされます。
Android開発者

また、質問を更新して、サンプルプロジェクトとビデオを含むバグレポートへのリンクを追加しました。これは、これが発生する他の理由が見当たらないため、これはバグだと思います。
Android開発者

0

これは効率的ではありませんが、60秒の精度でより一貫性があるかもしれません。

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

このブロードキャストレシーバーがフォアグラウンドサービス内で使用されている場合は、毎分時間を確認して、アクションを実行するかどうかを決定できます。


フォアグラウンドサービスがある場合、なぜこれが必要なのですか?希望があれば、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);

他のアプリでは実行できないことに気づき、それが役立つかどうか知りたいので、すでにこれを試しました。うまくいきませんでした。質問を更新しました。
Android開発者

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/...を
Androidデベロッパー

あなたのコードをGitHubでチェックアウトしました。アプリがMoto Z2 Playの最近のものから削除された後、ブロードキャストレシーバーが機能します。Pixelで試すことはできますが、コードは問題ないようです。アプリケーションを強制停止すると、スケジュールされたアラームが削除されますが、これは強制停止されているすべてのアプリで発生します。
Yuriy Kulikov

すでに何度か表示しています。スケジュール設定後に行ったのは、最近のタスクから削除することだけです。エミュレーターとPixel 4の両方でそれを行いました
Android開発者

pendingShowListを使用して問題が回避されるかどうかを確認してください。回避できる場合は、回答を更新します。多分それは誰かのために役立つでしょう。
Yuriy Kulikov
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.