したがって、Android Oを使用すると、1時間に数回以上の位置情報の更新を受信する場合は、サービスをフォアグラウンドサービスとして実行する必要があります。
フォアグラウンドサービスを開始する古い方法は、Oで動作するように見えることに気付きました。
startForeground(NOTIFICATION_ID, getNotification());
ここの動作変更ガイドによると: https://developer.Android.com/preview/behavior-changes.html
NotificationManager.startServiceInForeground()メソッドは、フォアグラウンドサービスを開始します。フォアグラウンドサービスを開始する古い方法は機能しなくなりました。
新しい方法はOをターゲットとする場合にのみ機能しますが、Oをターゲットとするかどうかに関係なく、Oのデバイスでは古いメソッドがまだ機能しているようです。
Edit例を含む:
GoogleサンプルプロジェクトLocationUpdatesForegroundServiceには、実際に問題を直接確認できる実用的な例があります。 https://github.com/googlesamples/Android-play-location/tree/master/LocationUpdatesForegroundService
StartForegroundメソッドは、APIレベル25 ORをターゲットにしてコンパイルし、Oをターゲットにしてコンパイルしても問題なく動作するようです(こちらを参照してください: https://developer.Android.com/preview/ migration.html#uya )
したがって、再現するには:
サービスはフォアグラウンドで実行されています(通知シェードのアイコンで表示)。 Oを実行しているデバイスでも、位置情報の更新は予想どおり(10秒ごとに)行われます。
これは私のために働いた。
- Activityクラスで、startService()の代わりにstartForegroundService()を使用してサービスを開始します
Intent myService = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(myService);
} else {
startService(myService);
}
- これで、サービスクラスのonStartCommand()で次のように実行します
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
......
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, Android_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
}
return START_NOT_STICKY;
}
注:NotificationCompat.Builderの代わりにNotification.Builderを使用すると機能しました。 Notification.Builderでのみ、Android Oreoの新機能であるチャンネルIDを提供する必要があります。
うまくいくことを願っています!
アクティビティ(またはフォアグラウンドサービスを開始する任意のコンテキスト)で、これを呼び出します:
Intent intent = new Intent(this, MyService.class)
ContextCompat.startForegroundService(context, intent);
サービスが開始されたら、 Android docs say と同様のコードを使用して通知チャネルを作成し、ビルドを作成してこれを使って:
final Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID).setSmallIcon(...)//
.setPriority(...).setCategory(...).setContentTitle(...).setContentText(...).setTicker(...);
// and maybe other preparations to the notification...
startForeground(notificationId, builder.build());
通常、startService
を使用して、放送受信機からサービスを開始します。バックグラウンドの制限があるため、startService
を呼び出すことはもはや不可能(または信頼性が高い)であるため、代わりにstartServiceInForeground
を呼び出す必要があります。ただし、ドキュメントからは、ブロードキャストインテントを受信するとアプリがホワイトリストに登録されるため、いつ発生するかは明確ではないため、startService
がIllegalStateException
をスローするタイミングは明確ではありません。
また、コメントで言及された@Kislingk NotificationManager.startServiceInForeground
は削除されました。 commit 08992ac で非推奨としてマークされました。
コミットメッセージから:
サービスを直接フォアグラウンド状態に開始するためにアプリオリ通知を提供する必要はなく、バックグラウンド実行状態からでも進行中のサービス作業を引き受けるために2段階の複合操作を採用します。 Context#startForegroundService()は、5秒以内にstartForeground()を介してサービスが正式にフォアグラウンド状態に入るという要件により、バックグラウンドの制限を受けません。サービスがそうしない場合、OSによって停止され、アプリはサービスANRで非難されます。
フォアグラウンドサービスを開始する従来の方法は、アプリがフォアグラウンドにある場合でも機能しますが、APIレベル26/Android Oを対象とするアプリのフォアグラウンドサービスを開始する推奨方法は、新しく導入されたNotificationManager#startServiceInForegroundメソッドを使用してフォアグラウンドサービスを作成することですそもそも。
Android Oのバックグラウンド実行制限のため、アプリがバックグラウンドモードの場合、バックグラウンドでサービスを開始してからフォアグラウンドにプロモートする古い方法は機能しません。
移行プロセスと手順はここに記載されています。 https://developer.Android.com/preview/features/background.html#migration
startForeground(1、notification); Android Oで機能しますが、Android O要件に従って、ユーザーに永続的な通知を表示する必要があります。同時に、場合によってはユーザーを混乱させる可能性があり(バックグラウンドで実行され、バッテリーに影響を与えるアプリに関するシステム通知)、ユーザーがアプリをアンインストールする可能性があります。したがって、WorkManagerクラスを新たに導入して、タスクをフォアグラウンドとしてスケジュールすることをお勧めします。
コードスニペット:
OneTimeWorkRequest work =
new OneTimeWorkRequest.Builder(MyWorker.class)
.build();
WorkManager.getInstance().enqueue(work);
バックスタックビルダーで必要な場合はサンプルを追加します
val notifyManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val playIntent = Intent(this, this::class.Java).setAction(PAUSE)
val cancelIntent = Intent(this, this::class.Java).setAction(EXIT)
val stop = PendingIntent.getService(this, 1, playIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val exit = PendingIntent.getService(this, 2, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notifyManager.createNotificationChannel(NotificationChannel(NOTIFICATION_ID_CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH))
NotificationCompat.Builder(this, NOTIFICATION_ID_CHANNEL_ID)
} else
NotificationCompat.Builder(this)
builder.apply {
setContentTitle(station.name)
setContentText(metaToText(meta) )
setSmallIcon(R.drawable.ic_play_arrow_white_24px)
setAutoCancel(false)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) priority = Notification.PRIORITY_MAX
addAction(R.drawable.ic_stop_white_24px, getString(R.string.player_notification_stop), stop)
addAction(R.drawable.ic_close_white_24px, getString(R.string.player_notification_exit), exit)
}
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addParentStack(PlayerActivity::class.Java)
stackBuilder.addNextIntent(Intent(this, PlayerActivity::class.Java))
builder.setContentIntent(stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT))
startForeground(NOTIFICATION_ID, builder.build())