Nous avons développé une application Android qui implique un service en arrière-plan. Pour implémenter ce service en tâche de fond, nous avons utilisé IntentService
. Nous voulons que l'application interroge le serveur tous les 60 seconds
. Donc, dans la IntentService
, le serveur est interrogé dans une boucle while. À la fin de la boucle while, nous avons utilisé Thread.sleep(60000)
pour que la prochaine itération ne commence qu'après 60 secondes.
Mais dans la Logcat
, je vois que parfois il faut plus de 5 minutes à l'application pour se réveiller (sortir de ce sommeil et commencer la prochaine itération). Ce n'est jamais 1 minute
comme nous le voulons.
Quelle est la raison pour ça? Les services de base doivent-ils être mis en œuvre de manière différente?
Problème2
Android tue ce processus d'arrière-plan (service d'intention) après un certain temps. Je ne peux pas dire quand exactement. Mais parfois, cela prend des heures et des jours avant que le service d'arrière-plan ne soit tué. J'apprécierais que vous me disiez la raison de cela. Parce que les services ne sont pas faits pour être tués. Ils sont conçus pour fonctionner en arrière-plan aussi longtemps que nous le voulons.
Code:
@Override
protected void onHandleIntent(Intent intent) {
boolean temp=true;
while(temp==true) {
try {
//connect to the server
//get the data and store it in the sqlite data base
}
catch(Exception e) {
Log.v("Exception", "in while loop : "+e.toString());
}
//Sleep for 60 seconds
Log.v("Sleeping", "Sleeping");
Thread.sleep(60000);
Log.v("Woke up", "Woke up");
//After this a value is extracted from a table
final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
cur.moveToLast();
String present_value=cur.getString(0);
if(present_value==null) {
//Do nothing, let the while loop continue
}
else if( present_value.equals("false") || present_value.equals("False") ) {
//break out of the while loop
db.close();
temp=false;
Log.v("run_in_bg", "false");
Log.v("run_in_bg", "exiting while loop");
break;
}
}
}
Mais chaque fois que le service est tué, cela se produit lorsque le processus est endormi. Le dernier journal lit - Sleeping : Sleeping
. Pourquoi le service est tué?
Le problème principal est que nous ne pouvons pas dire
Les services ne sont pas faits pour être tués. Ils sont conçus pour fonctionner en arrière-plan aussi longtemps que nous le voulons.
Fondamentalement, ce n'est pas vrai. Le système peut toujours mettre fin au service avec peu de mémoire et éventuellement d’autres situations .. Il existe deux façons de remédier à cela:
onStartCommand()
et renvoyez START_STICKY
comme résultat. Il dira au système que même s'il souhaite tuer votre service en raison d'une mémoire insuffisante, il devrait le recréer dès que la mémoire sera revenue à la normale.Bonne chance
Vous pouvez utiliser ScheduledExecutorService spécialement conçu à cet effet.
N'utilisez pas les timers, comme indiqué dans "Java Concurrency in Practice" ils peuvent être très imprécis.
IntentService
n'est pas destiné à continuer à fonctionner dans une boucle while
. L'idée est de réagir à une Intent
, d'effectuer un traitement et d'arrêter le service une fois terminé.
Cela ne signifie pas que cela ne fonctionne pas et je ne peux pas vous dire pourquoi vous voyez des retards aussi longs, mais la solution la plus propre consiste à utiliser une source externe pour contrôler le service périodiquement. Outre les méthodes Java de Vanilla, vous pouvez également consulter le AlarmManager
ou un Handler
comme indiqué dans la documentation AlarmManager
.
La manière Handler
fonctionnerait comme ceci
public class TriggerActivity extends Activity implements Handler.Callback {
// repeat task every 60 seconds
private static final long REPEAT_TIME = 60 * 1000;
// define a message id
private static final int MSG_REPEAT = 42;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(this);
}
@Override
protected void onStart() {
super.onStart();
// start cycle immediately
mHandler.sendEmptyMessage(MSG_REPEAT);
}
@Override
protected void onStop() {
super.onStop();
// stop cycle
mHandler.removeMessages(MSG_REPEAT);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler = null;
}
@Override
public boolean handleMessage(Message msg) {
// enqueue next cycle
mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
// then trigger something
triggerAction();
return true;
}
private void triggerAction() {
// trigger the service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
startService(serviceIntent);
}
}
Un simple Activity
(qui pourrait être étendu pour avoir cette fonctionnalité dans toutes vos activités) qui s'envoie une Message
tout le temps pendant son exécution (ici entre onStart
et onStop
)
Une meilleure solution serait de déclencher un AlarmManager toutes les 60 secondes. AlarmManager démarre ensuite le service qui interroge le serveur, puis lance un nouveau AlarmManager, une solution récursive qui fonctionne assez bien.
Cette solution sera plus fiable, car le système d’exploitation Android ne vous menacera pas, vous menaçant. Selon l'API: Alarm Manager est conçu pour les cas dans lesquels vous voulez que votre code d'application soit exécuté à une heure spécifique, même si votre application n'est pas en cours d'exécution.
Dans votre interface utilisateur/activité principale, etc., configurez ce minuteur pour qu'il s'éteigne dans 60 secondes:
long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds
Dans yourservice.class
, vous pourriez avoir ceci, il vérifie l'état de la connexion, s'il est bon, le temporisateur s'éteindra dans 60 secondes:
public class yourservice extends IntentService {
public yourservice() { //needs this constructor
super("server checker");
}
@Override
protected void onHandleIntent(Intent intent) {
WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(check for a certain condition your app needs etc){
//could check connection state here and stop if needed etc
stopSelf(); //stop service
}
else{ //poll the server again in 60 seconds
long ct = System.currentTimeMillis();
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi);
stopSelf(); //stop service since its no longer needed and new alarm is set
}
}
}
Les services sont tués. Comme l'application se fait tuer. Selon la philosophie Android, vous pouvez être tué à tout moment.
Comme d’autres l’ont écrit, ne supposez pas que votre service d’arrière-plan fonctionne pour toujours.
Mais vous pouvez utiliser un service fore ground pour réduire considérablement le risque d'être tué/redémarré. Notez que cela force une notification qui est toujours visible. Par exemple, les lecteurs de musique, les applications VPN et Sportstracker utilisent cette API.
Pour le problème 1, de Vanilla Java, Thread.Sleep()
est garanti pour réactiver le thread après son expiration, mais pas exactement après son expiration, il peut être plus tard dépendant principalement des statuts des autres threads, de la priorité, etc. Donc, si vous dormez une seconde dans votre fil, il dormira au moins une seconde, mais cela peut être 10 en fonction de nombreux facteurs. Je ne suis pas très au courant du développement Android, mais je suis presque sûr que c'est la même situation. .
Pour le problème 2, les services peuvent être supprimés lorsque la mémoire devient faible ou manuellement par l'utilisateur. Par conséquent, comme d'autres l'ont fait remarquer, utiliser AlarmManager pour redémarrer votre service après un certain temps vous aidera à le faire fonctionner tout le temps.
Vous pouvez essayer d'implémenter Jobscheduler avec JobService exécuté en arrière-plan, ce qui est recommandé ci-dessus sous Android O.
On dirait que vous devriez utiliser un Service
au lieu d’un IntentService
mais si vous voulez utiliser un IntentService
et le faire fonctionner toutes les 60 secondes, vous devez utiliser le AlarmManager
au lieu de il suffit de demander à la Thread
de dormir .. IntentService
s veulent s’arrêter, laissons-la et laisser AlarmManager
la réactiver quand elle devrait fonctionner à nouveau.
It could be probably for two reasons..
temp==true
Ajoute à cela des threads, ce qui crée long delays
jusqu'à 6 seconds
. Si le système fonctionne pour une base de données volumineuse, créer long delays
entre chaque requête ajoutera de la mémoire système . Si énorme que le system memory gets low
, le système doit terminate the process
..
Solution au problème ..
Vous pouvez remplacer ci-dessus par Alarm Manager
pour révoquer les services système après un intervalle de temps donné en utilisant Alarm Manager.
START_REDELIVER_INTENT
. Il s'agit de récupérer votre dernière intention de travail une fois l'application terminée. pour son utilisation, étudiez https://developer.Android.com/reference/Android/app/Service.html#START_REDELIVER_INTENTAndroid est plutôt doué pour éliminer les services de longue durée. J'ai trouvé WakefulIntentService de CommonsWare utile dans mon application: https://github.com/commonsguy/cwac-wakeful
Il vous permet de spécifier un intervalle de temps comme vous essayez de le faire en veille.