Mit Android O müssen Sie also Ihren Dienst als Vordergrunddienst ausführen, wenn Sie mehr als nur wenige Standortaktualisierungen pro Stunde erhalten möchten.
Mir ist aufgefallen, dass die alte Methode zum Starten eines Vordergrunddienstes auf O ..__ zu funktionieren scheint.
startForeground(NOTIFICATION_ID, getNotification());
Gemäß dem Verhaltensänderungen-Handbuch hier: https://developer.Android.com/preview/behavior-changes.html
Die NotificationManager.startServiceInForeground () - Methode startet einen Vordergrunddienst. Die alte Art, einen Vordergrunddienst zu starten, funktioniert nicht mehr.
Obwohl die neue Methode nur für O-Targeting funktioniert, scheint die alte Methode immer noch auf einem O-Gerät zu funktionieren, egal ob O-Targeting oder nicht.
Edit Einschließlich Beispiel:
Das Google-Beispielprojekt LocationUpdatesForegroundService enthält ein Arbeitsbeispiel, in dem Sie das Problem aus erster Hand sehen können . https://github.com/googlesamples/Android-play-location/tree/master/LocationUpdatesForegroundService
Die startForeground-Methode scheint ohne Probleme zu funktionieren, ob das Targeting und Compilieren gegen API Level 25 OR, das Targeting und das Compilieren gegen O (wie hier angegeben: https://developer.Android.com/preview/migration.html) #uya )
Also zu reproduzieren:
Der Dienst wird im Vordergrund ausgeführt (durch ein Symbol im Benachrichtigungsschatten angezeigt). Standortaktualisierungen werden wie erwartet (alle 10 Sekunden) auch auf einem Gerät ausgeführt, auf dem O ausgeführt wird. Was fehlt mir hier?
Das hat bei mir funktioniert.
- Starten Sie in der Aktivitätsklasse den Dienst mithilfe von startForegroundService () anstelle von startService ()
Intent myService = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(myService);
} else {
startService(myService);
}
- Jetzt in der Service-Klasse in onStartCommand () wie folgt vorgehen
@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;
}
Hinweis: Die Verwendung von Notification.Builder anstelle von NotificationCompat.Builder hat dazu geführt, dass es funktioniert. Nur in Notification.Builder müssen Sie die Kanal-ID angeben, eine neue Funktion in Android Oreo.
Hoffe, es funktioniert!
Rufen Sie in der Aktivität (oder in einem beliebigen Kontext, der den Vordergrunddienst startet) Folgendes auf:
Intent intent = new Intent(this, MyService.class)
ContextCompat.startForegroundService(context, intent);
Wenn der Dienst gestartet wurde, erstellen Sie einen Benachrichtigungskanal mit ähnlichem Code wie Android-Dokumente . Erstellen Sie dann einen Build und verwenden Sie ihn:
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());
Normalerweise starten Sie Ihren Dienst von einem Rundfunkempfänger mit startService
. Sie sagen, dass es nicht mehr möglich (oder zuverlässig) ist, startService
aufzurufen, da es jetzt Hintergrundbeschränkungen gibt. Sie müssen stattdessen startServiceInForeground
aufrufen. Aus Dokumenten ist jedoch nicht wirklich klar, wann dies passiert, weil die App beim Empfang einer Broadcast-Absicht in die Positivliste aufgenommen wird. Daher ist nicht genau bekannt, wann startService
IllegalStateException
wirft.
Die ältere Methode zum Starten eines Vordergrunddiensts funktioniert immer noch, wenn sich die App im Vordergrund befindet. Die empfohlene Methode zum Starten eines Vordergrunddiensts für Apps, die API 26/Android O verwenden, ist die Verwendung der neu eingeführten NotificationManager # startServiceInForeground-Methode zum Erstellen eines Vordergrunddiensts an erster Stelle.
Die alte Methode, den Dienst im Hintergrund zu starten und dann in den Vordergrund zu bringen, funktioniert nicht, wenn sich die App im Hintergrundmodus befindet, da Android O im Hintergrund ausgeführt wird.
Der Migrationsprozess und die Schritte werden hier dokumentiert. https://developer.Android.com/preview/features/background.html#migration
Wie auch @Kislingk in einem Kommentar erwähnt, wurde NotificationManager.startServiceInForeground
entfernt. Es wurde als veraltet mit dem Commit 08992ac markiert.
Aus der Commit-Nachricht:
Anstatt eine a-priori-Benachrichtigung zu verlangen, werden Starten Sie einen Dienst direkt in den Vordergrundzustand, übernehmen wir eine zweistufige Mischbetrieb für laufende Servicearbeiten auch von einem Hintergrundausführungszustand. Kontext # startForegroundService () ist nicht Hintergrundbeschränkungen unterliegen, mit der Anforderung, dass die Der Dienst gibt den Vordergrundzustand formal über startForeground () in .__ ein. 5 Sekunden. Wenn der Dienst dies nicht tut, wird er vom Betriebssystem und von .__ angehalten. Die App wird mit einem Dienst ANR beschuldigt.
startForeground (1, Benachrichtigung); funktioniert für Android O, aber gemäß Android O-Anforderung müssen wir dem Benutzer eine permanente Benachrichtigung anzeigen. Gleichzeitig kann es den Benutzer in einigen Fällen verwirren (Systembenachrichtigung über im Hintergrund ausgeführte App und Auswirkungen auf die Batterie), sodass der Benutzer die App deinstallieren kann. Am besten wäre es also, die WorkManager-Klasse neu einzuführen, um die Aufgabe als Vordergrund zu planen.
Code-Auszug:
OneTimeWorkRequest work =
new OneTimeWorkRequest.Builder(MyWorker.class)
.build();
WorkManager.getInstance().enqueue(work);
Ich füge ein Muster hinzu, wenn ein Bedarf mit Backstack Builder besteht
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())