web-dev-qa-db-fra.com

Comment pouvons-nous empêcher un service d'être tué par OS?

J'utilise Service dans mon application et elle doit s'exécuter jusqu'à ce que mon application soit désinstallée, mais le problème est qu'elle est tuée par le système d'exploitation.

Comment pouvons-nous l'empêcher d'être tué par OS? Ou s'il est tué, pouvons-nous redémarrer ce service par programme?

44
Rahul

Vous pouvez exécuter le service dans le premier plan en utilisant startForeground () .

Un service de premier plan est un service qui est considéré comme quelque chose dont l'utilisateur est activement au courant et qui n'est donc pas un candidat que le système doit tuer lorsqu'il manque de mémoire.

Mais gardez à l'esprit qu'un service de premier plan doit fournir une notification pour la barre d'état ( lire ici ), et que la notification ne peut être rejetée que si le service est arrêté ou supprimé du premier plan.

Remarque: Cela ne garantit pas absolument que le service ne sera pas tué dans des conditions de mémoire extrêmement faibles. Cela ne fait que le rendre moins susceptible d'être tué.

42
Dheeraj V.S.

J'ai été récemment perplexe face au même problème que le vôtre, mais maintenant, j'ai trouvé une bonne solution. Tout d'abord, vous devez savoir que même votre service a été tué par OS, la méthode onCreate de votre service serait appelée par OS dans un court laps de temps, vous pouvez donc faire quelque chose avec la méthode onCreate comme ceci:

@Override
public void onCreate() {
    Log.d(LOGTAG, "NotificationService.onCreate()...");
    //start this service from another class
    ServiceManager.startService();
}
@Override
public void onStart(Intent intent, int startId) {
    Log.d(LOGTAG, "onStart()...");
    //some code of your service starting,such as establish a connection,create a TimerTask or something else
}

le contenu de "ServiceManager.startService ()" est:

public static void startService() {
    Log.i(LOGTAG, "ServiceManager.startSerivce()...");
    Intent intent = new Intent(NotificationService.class.getName());
    context.startService(intent);
}

Cependant, cette solution est uniquement disponible pour la situation où votre service est tué par GC.Parfois, notre service peut être tué par l'utilisateur avec Program Manager.Dans cette situation, vos prosses seront tués et votre service ne sera jamais réinstancié. Votre service ne peut donc pas être redémarré. Mais la bonne nouvelle est que lorsque le PM tue votre service, il appellera votre méthode onDestroy. Nous pouvons donc faire quelque chose avec cette méthode.

    @Override
public void onDestroy() {
    Intent in = new Intent();
    in.setAction("YouWillNeverKillMe");
    sendBroadcast(in);
    Log.d(LOGTAG, "onDestroy()...");
}

La chaîne de "YouWillNeverKillMe" est une action personnalisée. La chose la plus importante de cette méthode est de ne pas ajouter de code avant d'envoyer la diffusion. Comme le système n'attendra pas la fin de onDestroy (), vous devez envoyer la diffusion dès que possible. Enregistrez ensuite un récepteur dans manifast.xml:

<receiver Android:name=".app.ServiceDestroyReceiver" >
        <intent-filter>
            <action Android:name="YouWillNeverKillMe" >
            </action>
        </intent-filter>
    </receiver>

Enfin, créez un BroadcastReceiver et démarrez votre service dans la méthode onReceive:

@Override
public void onReceive(Context context, Intent intent) {
    Log.d(LOGTAG, "ServeiceDestroy onReceive...");
    Log.d(LOGTAG, "action:" + intent.getAction());
    Log.d(LOGTAG, "ServeiceDestroy auto start service...");
    ServiceManager.startService();
}

J'espère que cela vous sera utile et excusez mon pauvre anglais écrit.

22
Alaowan

Remplacez la méthode onStartCommand() dans votre classe de service et retournez simplement START_STICKY (Comme suggéré par "Ce n'est pas vide"). C'est tout ce dont vous avez besoin. Si le processus qui exécute votre service est tué (par une condition de mémoire insuffisante par exemple), le système Android le redémarrera automatiquement (généralement avec un certain retard, comme 5 secondes).

N'utilisez plus onStart() comme suggéré dans une autre réponse, c'est obsolète.

11
Chris

utilisation

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    //**Your code **
    // We want this service to continue running until it is explicitly
    // stopped, so return sticky.
    return START_STICKY;
} 

ref Documentation cycle de vie du service.

Modifier la méthode ajoutée.

9
Its not blank

Pour autant que je sache, onDestroy() ne sera appelée que lorsque le service est explicitement arrêté (Force Stop). Mais cette méthode ne sera pas appelée au cas où le service serait tué par le système d'exploitation/en balayant la liste des applications récentes. Dans ces cas, un autre gestionnaire d'événements nommé onTaskRemoved(Intent) est appelé. Cela est dû à un défaut dans Android 4.3-4.4 selon le lien ici . Essayez d'utiliser le code ci-dessous: -

public void onTaskRemoved(Intent intent){
super.onTaskRemoved(intent);
Intent intent=new Intent(this,this.getClass()); 
startService(intent); 
}
5
Karthik

J'ai trouvé une autre solution au problème qui garantit que votre service sera toujours en vie. Dans mon cas, ce schéma résout également le problème avec FileObserver, qui arrête de fonctionner après un certain temps.

  1. Utilisez une activité (StartServicesActivity) pour démarrer le service (FileObserverService) en tant que service de premier plan.
  2. Utilisez la classe BroadcastReceiver (dans l'exemple CommonReceiver) pour redémarrer votre service dans certaines situations spéciales et au cas où il serait tué.

J'ai utilisé ce code dans mon application "Envoyer automatiquement des photos par e-mail" https://play.google.com/store/apps/details?id=com.alexpap.EmailPicturesFree

Voici la classe CommonReceiver.

public class CommonReceiver extends BroadcastReceiver {

    public void onReceive(Context paramContext, Intent paramIntent)
    {
        paramContext.startService(new Intent(paramContext, FileObserverService.class));
    }
}

Voici sa définition dans AndroidManifest.xml juste avant la balise de fermeture de l'application.

<receiver Android:name="com.alexpap.services.CommonReceiver">
    <intent-filter>
        <action Android:name="Android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
    <intent-filter>
        <action Android:name="Android.net.conn.CONNECTIVITY_CHANGE"/>
    </intent-filter>
    <intent-filter>
        <action Android:name="Android.intent.action.USER_PRESENT"/>
    </intent-filter>
</receiver>

Démarrez le service dans l'activité StartServicesActivity.

Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
StartServicesActivity.this.startService(iFileObserver);

Voici la méthode onStartCommand () du service.

public int onStartCommand(Intent intent, int flags,  int startId) {

    int res = super.onStartCommand(intent, flags, startId);

    /*** Put your code here ***/

    startServiceForeground(intent, flags, startId);

    return Service.START_STICKY;  
}

public int startServiceForeground(Intent intent, int flags, int startId) {

    Intent notificationIntent = new Intent(this, StartServicesActivity.class); 
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this)
        .setContentTitle("File Observer Service")
        .setContentIntent(pendingIntent)
        .setOngoing(true)
            .build();

    startForeground(300, notification);

    return START_STICKY;
}

J'ai testé ce code à l'aide de l'application Task Killer, et chaque fois que le service était tué, il était redémarré presque immédiatement (exécute onStartCommand ()). Il est redémarré également à chaque fois que vous allumez le téléphone et après le redémarrage. J'utilise ce code dans mon application, qui envoie par e-mail chaque photo que vous prenez avec votre téléphone pour prédéfinir la liste des e-mails. L'e-mail d'envoi et la liste des e-mails de réception sont définis dans une autre activité et sont stockés dans les préférences partagées. J'ai pris environ 100 photos en plusieurs heures et toutes ont été envoyées correctement pour recevoir des e-mails.

5
AlexPap
@Override
public void onDestroy() {

    super.onDestroy();
    startService(new Intent(this, YourService.class));

}

écrivez le code ci-dessus dans votre service et votre service ne s'arrêtera jamais, même les utilisateurs veulent le détruire ou ils veulent le tuer, il ne tuera jamais jusqu'à ce que votre application ne soit pas désinstallée de votre appareil

Vous pouvez essayer de démarrer votre service à plusieurs reprises, par exemple toutes les 5 secondes. De cette façon, lorsque votre service est en cours d'exécution, il exécutera onStartCommand () toutes les 5 secondes. J'ai testé ce schéma et il est très fiable, mais malheureusement il augmente légèrement les frais généraux du téléphone. Voici le code de votre activité où vous démarrez le service.

Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
PendingIntent pendingIntentFileObserver = PendingIntent.getService(StartServicesActivity.this, 0, iFileObserver, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Date now = new Date();

    //start every 5 seconds
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTime(), 5*1000, pendingIntentFileObserver);

Et voici onStartCommand () du service.

//class variable
public static boolean isStarted = false;

public int onStartCommand(Intent intent, int flags,  int startId) {

    int res = super.onStartCommand(intent, flags, startId);

    //check if your service is already started
    if (isStarted){      //yes - do nothing
        return Service.START_STICKY;
    } else {             //no 
        isStarted = true;
    }


    /**** the rest of your code  ***/

    return Service.START_STICKY;

}
2
AlexPap

Créez d'abord le service dans un autre processus et écrivez le diffuseur qui s'exécute en récursivité à intervalles de temps

protected CountDownTimer rebootService = new CountDownTimer(9000, 9000) {


    @Override
    public void onTick(long millisUntilFinished) {


    }

    @Override
    public void onFinish() {

        sendBroadcast(reboot);
        this.start();
        Log.d(TAG, "rebootService sending PREVENT AUTOREBOT broadcast");


    }

};

Après l'enregistrement du récepteur de diffusion dans le processus principal également avec une récursion de temporisation qui est lancée après l'arrivée de la première diffusion du service

protected static class ServiceAutoRebooter extends BroadcastReceiver {

    private static   ServiceAutoRebooter instance = null;
    private  RebootTimer rebootTimer = null;

    private static ServiceAutoRebooter getInstance() {

        if (instance == null) {

            instance = new ServiceAutoRebooter();

        }
        return instance;

    }





    public class RebootTimer extends CountDownTimer {

        private Context _context;
        private Intent _service;


        public RebootTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }


        @Override
        public void onTick(long millisUntilFinished) {

        }


        @Override
        public void onFinish() {

            _context.startService(_service);
            this.cancel();
            Log.d(TAG, "Service AutoRebooted");


        }


    }


    @Override
    public void onReceive(Context context, Intent intent) {

        if (rebootTimer == null) {
            Log.d(TAG, "rebootTimer == null");

            rebootTimer = new RebootTimer(10000, 10000);
            rebootTimer._context = context;

            Intent service = new Intent(context, SomeService.class);
            rebootTimer._service = service;
            rebootTimer.start();

        } else {

            rebootTimer.cancel();
            rebootTimer.start();
            Log.d(TAG, "rebootTimer is restarted");

        }


    }


}

Le service sera redémarré automatiquement si le temps au RebootTimer (processus principal) expire, ce qui signifie que la diffusion "PREVENT AUTOREBOT" du service n'est pas arrivée

0