Je veux garder un IntentService
en tâche de fond même lorsque l'application est supprimée. Et par "tué" je veux dire appuyez sur le bouton d'accueil pendant un long moment -> voir toutes les applications en cours d'exécution -> balayez mon application de côté -> application supprimée OR appuyez sur le bouton Précédent pendant longtemps -> application supprimée
Mon code va comme suit. Dans mon activité principale:
Intent intent = new Intent(this, MyService.class);
this.startService(intent);
Dans mon MyService:
public class MyService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
System.out.println("MyService started");
run();
}
private void run() {
while (true){
System.out.println("MyService still running");
doSomething();
waitSomeTime();
}
}
}
Je vois que le service est en cours d'exécution lorsque l'application est ouverte. Il est toujours en cours d'exécution lorsque je minimise l'application via le bouton d'accueil. Il est toujours en cours d'exécution quand je ferme l'application via le bouton Précédent. Mais ça va s'arrêter si je le tue comme mentionné ci-dessus. Comment résoudre ce problème?
Si votre application est lancée par votre application, votre service s'exécute en réalité sur le processus principal. Ainsi, lorsque l'application sera supprimée, le service sera également arrêté. Vous pouvez donc envoyer la diffusion à partir de la méthode onTaskRemoved
de votre service de la manière suivante:
Intent intent = new Intent("com.Android.ServiceStopped");
sendBroadcast(intent);
et avoir un récepteur de diffusion qui démarrera à nouveau un service. J'ai essayé le service redémarre à partir de tout type de destruction.
Toutes les réponses semblent correctes donc je vais aller de l'avant et donner un complet répondez ici.
Premièrement, le moyen le plus simple de faire ce que vous essayez de faire est de lancer une diffusion dans Android lorsque app est tué manuellement, et définissez une coutume BroadcastReceiver
pour déclencher un redémarrage du service suivant.
Passons maintenant au code.
Créez votre service dans YourService.Java
Notez la méthode onCreate()
, où nous démarrons un service de premier plan différemment pour les versions de build supérieures à Android Oreo. Ceci en raison des règles de notification strictes récemment introduites, selon lesquelles nous devons définir notre propre canal de notification pour les afficher correctement.
La this.sendBroadcast(broadcastIntent);
de la méthode onDestroy()
est l'instruction qui envoie de manière asynchrone une diffusion avec le nom de l'action "restartservice"
. Nous utiliserons cela plus tard comme déclencheur pour redémarrer notre service.
Ici, nous avons défini une tâche simple de minuterie, qui affiche une valeur de compteur toutes les 1 seconde dans le Log
en s’incrémentant à chaque impression.
public class YourService extends Service {
public int counter=0;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}
@RequiresApi(Build.VERSION_CODES.O)
private void startMyOwnForeground()
{
String NOTIFICATION_CHANNEL_ID = "example.permanence";
String channelName = "Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
stoptimertask();
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
timer = new Timer();
timerTask = new TimerTask() {
public void run() {
Log.i("Count", "========= "+ (counter++));
}
};
timer.schedule(timerTask, 1000, 1000); //
}
public void stoptimertask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Créez un récepteur de diffusion pour répondre à vos émissions personnalisées définies dans Restarter.Java
La diffusion avec le nom de l'action "restartservice"
que vous venez de définir dans YourService.Java
Est maintenant supposée déclencher une méthode qui redémarrez votre service . Ceci est fait en utilisant BroadcastReceiver
dans Android.
Nous remplaçons la méthode onRecieve()
intégrée dans BroadcastReceiver
pour ajouter l'instruction qui redémarrera le service. La startService()
ne fonctionnera pas comme prévu à partir de Android Oreo 8.1, en tant que strict conditions de fond va bientôt mettre fin au service après le redémarrage, une fois l'application tuée. Par conséquent, nous utilisons startForegroundService()
pour les versions supérieures et affichons une notification continue pour que le service continue à fonctionner.
public class Restarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("Broadcast Listened", "Service tried to stop");
Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, YourService.class));
} else {
context.startService(new Intent(context, YourService.class));
}
}
}
Définissez votre MainActivity.Java
Pour appeler le service au démarrage de l'application.
Ici, nous définissons une méthode isMyServiceRunning()
séparée pour vérifier le statut actuel du service en arrière-plan. Si le service est en cours d'exécution pas, nous le démarrons en utilisant startService()
.
Étant donné que l'application fonctionne déjà à l'avant-plan, nous n'avons pas besoin de lancer le service en tant que service à l'avant-plan pour éviter son interruption.
Notez que dans onDestroy()
, nous appelons dédoublement stopService()
, de sorte que notre méthode surchargée est invoqué. Si cela n’avait pas été fait, le service aurait pris fin automatiquement après la suppression de l’application sans appeler notre méthode modifiée onDestroy()
dans YourService.Java
public class MainActivity extends AppCompatActivity {
Intent mServiceIntent;
private YourService mYourService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mYourService = new YourService();
mServiceIntent = new Intent(this, mYourService.getClass());
if (!isMyServiceRunning(mYourService.getClass())) {
startService(mServiceIntent);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("Service status", "Running");
return true;
}
}
Log.i ("Service status", "Not running");
return false;
}
@Override
protected void onDestroy() {
stopService(mServiceIntent);
super.onDestroy();
}
}
Enfin, enregistrez-les dans votre AndroidManifest.xml
Les trois classes ci-dessus doivent être enregistrées séparément dans AndroidManifest.xml
.
Notez que nous définissons un intent-filter
Avec le nom de l'action comme "restartservice"
Où le Restarter.Java
est enregistré en tant que receiver
. Cela garantit que notre coutume BroadcastReciever
est appelée chaque fois que le système rencontre une émission avec le nom de l'action =.
<application
Android:allowBackup="true"
Android:icon="@mipmap/ic_launcher"
Android:label="@string/app_name"
Android:supportsRtl="true"
Android:theme="@style/AppTheme">
<receiver
Android:name="Restarter"
Android:enabled="true"
Android:exported="true">
<intent-filter>
<action Android:name="restartservice" />
</intent-filter>
</receiver>
<activity Android:name="MainActivity">
<intent-filter>
<action Android:name="Android.intent.action.MAIN" />
<category Android:name="Android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
Android:name="YourService"
Android:enabled="true" >
</service>
</application>
Cela devrait maintenant redémarrer votre service si l'application a été supprimée du gestionnaire de tâches. Ce service continuera à fonctionner en arrière-plan tant que l'utilisateur ne Force Stop
l'appli de Paramètres de l'application .
UPDATE: Félicitations à Dr.jacky pour le signaler. La façon susmentionnée ne fonctionnera que si la onDestroy()
du service est appelée, ce qui pourrait ne pas être le cas à certains moments, ce qui Je n'étais pas au courant. Merci.
Dans votre service, ajoutez le code suivant.
@Override
public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
à l'intérieur de la commande onstart put START_STICKY
... Ce service ne tuera pas s'il ne fait pas trop de travail et que le noyau veut le tuer pour ça ...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
La raison en est que vous essayez d'utiliser un service IntentService. Voici la ligne de la API Docs
IntentService effectue les opérations suivantes:
Arrête le service une fois que toutes les demandes de démarrage ont été traitées. Vous ne devez donc jamais appeler stopSelf ().
Ainsi, si vous souhaitez que votre service fonctionne indéfiniment, je vous suggère plutôt d’étendre la classe Service. Toutefois, cela ne garantit pas que votre service fonctionnera indéfiniment. Votre service aura toujours une chance d'être tué par le noyau dans un état de mémoire insuffisante s'il est prioritaire. Vous avez donc deux options:
1) Laissez-le en marche au premier plan en appelant la méthode startForeground()
.
2) Redémarrez le service s'il est tué. Voici une partie de l'exemple de la documentation où ils parlent de redémarrer le service après sa suppression.
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
Vous pouvez utiliser Android:stopWithTask="false"
dans le manifeste ci-dessous, cela signifie que même si l'utilisateur tue une application en la supprimant de la liste des tâches, votre service ne s'arrêtera pas.
<service Android:name=".service.StickyService"
Android:stopWithTask="false"/>