Je cible SDK version 27 avec une version minimale de 19 et tente d'obtenir un service qui fonctionne en permanence en arrière-plan. J'ai essayé différentes options de démarrage du service, mais l'application a tout de même été supprimée. J'ai essayé d'utiliser un BroadcastReceiver pour démarrer le service quand il a été tué, mais cela m'a donné une erreur disant que l'application était en arrière-plan et ne pouvait pas démarrer un service, alors j'ai essayé d'utiliser JobScheduler, ce qui m'a donné la même erreur. Comment cela est-il censé être fait? Par exemple, si je réalisais une application de podomètre, comment pourrais-je la laisser fonctionner en arrière-plan?
Dans oreo release Android défini limite aux services d'arrière-plan .
Pour améliorer l'expérience utilisateur, Android 8.0 (API niveau 26) impose des limitations sur ce que les applications peuvent faire lorsqu'elles s'exécutent en arrière-plan.
Néanmoins, si l'application doit toujours exécuter son service, nous pouvons créer un service de premier plan.
Limitations du service en arrière-plan: Lorsqu'une application est inactive, il existe des limites à son utilisation des services en arrière-plan. Ceci ne s'applique pas aux services de premier plan, qui sont plus visibles pour l'utilisateur.
Créez donc un service de premier plan . Dans lequel vous placerez une notification pour l'utilisateur pendant l'exécution de votre service. Voir cette réponse (Il y en a beaucoup d'autres)
Et maintenant, si vous ne voulez pas de notification pour votre service. Une solution est pour cela.
Vous pouvez créer une tâche périodique qui démarrera votre service. Le service fera son travail et s’arrêtera lui-même. De ce fait, votre application ne sera pas considérée comme épuisante.
Vous pouvez créer une tâche périodique avec Alarm Manager , planificateur de tâches , Evernote-Jobs ou Gestionnaire de travail .
J'ai créé un service permanent avec Work-Manager, qui fonctionne parfaitement.
Depuis Android 8.0, de nombreuses limitations du service en arrière-plan ont été introduites.
Deux solutions:
si vous avez besoin d'un contrôle total sur la tâche et le temps d'exécution, vous devez choisir Service d'arrière-plan . Avantages: votre application sera considérée comme étant en vie, il est donc plus improbable que le système d'exploitation le tue pour libérer des ressources. Inconvénients: votre utilisateur verra toujours la notification de premier plan.
si vous avez besoin de planifier périodiquement une tâche, alors Work Manager (introduit dans Google I/O 18) est la meilleure solution. Ce composant choisit le meilleur planificateur possible (Jobscheduler, JobDispatcher, AlarmManager ..). N'oubliez pas que les API de Work Manager ne sont utiles que pour les tâches nécessitant une exécution garantie et pouvant être différées. Réf.: Android Dev Documentation
En utilisant BroadcastReciever, nous pouvons exécuter le service backgrouund de manière continue, mais s'il est tué, détruit automatiquement l'instance de l'ancien service. Lorsque le service s'arrête de force, il appelle la méthode onDestroy (). Dans ce cas, utilisez un récepteur et envoyez une diffusion à chaque fois que le service détruire et redémarrer le service à nouveau. Dans la méthode suivante, com.Android.app est une action personnalisée de la classe reciever qui étend BroadcastReciever.
public void onDestroy() {
try {
myTimer.cancel();
timerTask.cancel();
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent("com.Android.app");
intent.putExtra("valueone", "tostoreagain");
sendBroadcast(intent);
}
et dans la méthode onReceive
@Override
public void onReceive(Context context, Intent intent) {
Log.i("Service Stoped", "call service again");
context.startService(new Intent(context, ServiceCheckWork.class));
}
Dans le cas où le périphérique est redémarré, l’action onBootCompleted du récepteur est capturée.
Lorsque vous ciblez SdkVersion "O"
Dans MainActivity.Java, définissez getPendingIntent ()
private PendingIntent getPendingIntent() {
Intent intent = new Intent(this, YourBroadcastReceiver.class);
intent.setAction(YourBroadcastReceiver.ACTION_PROCESS_UPDATES);
return PendingIntent.getBroadcast(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
ici, nous utilisons PendingIntent avec BroadcastReceiver et ce BroadcastReceiver a déjà été défini dans AndroidManifest.xml. Maintenant, dans la classe YourBroadcastReceiver.Java qui contient une méthode onReceive ():
Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PROCESS_UPDATES.equals(action)) {
NotificationResult result = NotificationResult.extractResult(intent);
if (result != null) {
List<Notification> notifications = result.getNotification();
NotificationResultHelper notificationResultHelper = new
NotificationResultHelper(
context, notifications);
// Save the notification data to SharedPreferences.
notificationResultHelper.saveResults();
// Show notification with the notification data.
notificationResultHelper.showNotification();
Log.i(TAG,
NotificationResultHelper.getSavedNotificationResult(context));
}
}
}
}
La seule solution que je suggérerais consiste à utiliser Firebase Cloud Messages. Ou des services de premier plan.
Pour cela, il suffit de démarrer un service de premier plan qui n’est visible que pendant une fraction de seconde et qui démarre votre service d’arrière-plan. Dans le service en arrière-plan, vous démarriez ensuite périodiquement le service au premier plan.
Avant de donner un exemple, vous devriez vraiment vous demander si c'est la bonne façon de procéder pour vous, il pourrait y avoir d'autres solutions à des problèmes donnés (comme utiliser JobIntentService, etc.); et gardez à l’esprit que c’est un hack, il se peut qu’il soit corrigé quelque temps et que je ne l’utilise généralement pas (je l’ai testé avec l’écran éteint et l’économie de batterie activée) et il est resté en vie tout le temps - mais cela pourrait empêcher votre appareil de somnoler .. encore une fois, c'est un sale bidouillage!)
Exemple:
public class TemporaryForegroundService extends Service {
public static final int NOTIFICATION_ID = 666;
private static Notification notification;
@Override
public void onCreate() {
super.onCreate();
if(notification == null)
notification = new NotificationCompat.Builder(this, NotificationChannels.importantChannel(this)).
setSmallIcon(R.mipmap.ic_launcher).setContentTitle("The unseen blade").setContentText("If you see me, congrats to you.").build();
startForeground(NOTIFICATION_ID, notification);
startService(new Intent(this, PermanentBackgroundService.class));
stopForeground(true);
stopSelf();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
public class PermanentBackgroundService extends Service {
private Runnable keepAliveRunnable = new Runnable() {
@Override
public void run() {
keepServiceAlive();
if(handler != null) handler.postDelayed(this, 15*1000);
}
};
private Handler handler;
public void onCreate(){
handler = new Handler();
handler.postDelayed(keepAliveRunnable, 30* 1000);
}
public void onDestroy() {
super.onDestroy();
keepServiceAlive();
}
private void keepServiceAlive() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
} else {
startService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
}
}
}
comme tu dis:
J'ai essayé d'utiliser un BroadcastReceiver pour démarrer le service quand il a été tué, mais cela m'a donné une erreur en disant que l'application était en arrière-plan et ne pouvait pas démarrer un service.
dans Oreo lorsque vous êtes en arrière-plan et que vous souhaitez démarrer un service, ce service doit être un service de premier plan, utilisez le code suivant:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
si vous utilisez ce code dans Oreo, vous disposez de quelques secondes dans onStartCommand
pour commencer au premier plan, sinon votre service est considéré comme ne répondant pas et peut être forcé par l'utilisateur (in Android 8 ou au dessus de)
Il n'est pas nécessaire d'utiliser BroadcastReceiver
pour démarrer le service après sa fermeture, il suffit de renvoyer START_STICKY
ou START_REDELIVER_INTENT
de onStartCommand
de votre service pour redémarrer le service après sa fermeture