J'en ai récemment écrit un et j'ai décidé que ce n'était pas une bonne idée de laisser un service d'arrière-plan en marche. Il sera probablement arrêté par le système d'exploitation de toute façon, ou il pourrait l'être. Ce que j'ai fait, c'est d'utiliser un filtre pour l'intention de démarrage, puis de définir une alarme à l'aide du gestionnaire d'alarmes afin que mon application soit redémarrée à intervalles réguliers, puis qu'elle envoie les données. Vous trouverez de bonnes informations sur les services et le gestionnaire d'alarmes dans la documentation Android.
Tout d'abord, j'ai créé un récepteur de diffusion qui démarre simplement mon service lorsqu'une connexion internet est ouverte (je ne suis intéressé que s'il y a une connexion - vous pourriez vouloir filtrer pour l'événement de démarrage aussi). Le récepteur de lancement doit être de courte durée, donc juste démarrer votre service :
public class LaunchReceiver extends BroadcastReceiver {
public static final String ACTION_PULSE_SERVER_ALARM =
"com.proofbydesign.homeboy.ACTION_PULSE_SERVER_ALARM";
@Override
public void onReceive(Context context, Intent intent) {
AppGlobal.logDebug("OnReceive for " + intent.getAction());
AppGlobal.logDebug(intent.getExtras().toString());
Intent serviceIntent = new Intent(AppGlobal.getContext(),
MonitorService.class);
AppGlobal.getContext().startService(serviceIntent);
}
}
Dans le manifeste, j'ai :
<receiver
android:name="LaunchReceiver"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="com.proofbydesign.homeboy.ACTION_PULSE_SERVER_ALARM" />
</intent-filter>
</receiver>
Remarquez que j'ai un filtre pour ma propre alarme, qui me permet de fermer le service et de le faire redémarrer après qu'il ait fait son travail.
Le haut du service de mon moniteur ressemble à ça :
public class MonitorService extends Service {
private LoggerLoadTask mTask;
private String mPulseUrl;
private HomeBoySettings settings;
private DataFile dataFile;
private AlarmManager alarms;
private PendingIntent alarmIntent;
private ConnectivityManager cnnxManager;
@Override
public void onCreate() {
super.onCreate();
cnnxManager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intentOnAlarm = new Intent(
LaunchReceiver.ACTION_PULSE_SERVER_ALARM);
alarmIntent = PendingIntent.getBroadcast(this, 0, intentOnAlarm, 0);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// reload our data
if (mPulseUrl == null) {
mPulseUrl = getString(R.string.urlPulse);
}
AppGlobal.logDebug("Monitor service OnStart.");
executeLogger();
}
executeLogger démarre une asyncTask, ce qui est probablement un excès de prudence de ma part (ce n'est que ma troisième application Android). L'asyncTask récupère les données GPS, les envoie à l'internet et enfin règle l'alarme suivante :
private void executeLogger() {
if (mTask != null
&& mTask.getStatus() != LoggerLoadTask.Status.FINISHED) {
return;
}
mTask = (LoggerLoadTask) new LoggerLoadTask().execute();
}
private class LoggerLoadTask extends AsyncTask<Void, Void, Void> {
// TODO: create two base service urls, one for debugging and one for live.
@Override
protected Void doInBackground(Void... arg0) {
try {
// if we have no data connection, no point in proceeding.
NetworkInfo ni = cnnxManager.getActiveNetworkInfo();
if (ni == null || !ni.isAvailable() || !ni.isConnected()) {
AppGlobal
.logWarning("No usable network. Skipping pulse action.");
return null;
}
// / grab and log data
} catch (Exception e) {
AppGlobal.logError(
"Unknown error in background pulse task. Error: '%s'.",
e, e.getMessage());
} finally {
// always set the next wakeup alarm.
int interval;
if (settings == null
|| settings.getPulseIntervalSeconds() == -1) {
interval = Integer
.parseInt(getString(R.string.pulseIntervalSeconds));
} else {
interval = settings.getPulseIntervalSeconds();
}
long timeToAlarm = SystemClock.elapsedRealtime() + interval
* 1000;
alarms.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToAlarm,
alarmIntent);
}
return null;
}
}
Je remarque que je n'appelle pas stopSelf() après avoir déclenché l'alarme, donc mon service va rester assis à ne rien faire jusqu'à ce qu'il soit arrêté par l'op sys. Comme je suis le seul utilisateur de cette application, cela n'a pas d'importance, mais pour une application publique, l'idée est de régler l'alarme pour le prochain intervalle, puis d'appeler stopSelf pour fermer.
Mise à jour Voir le commentaire de @juozas sur l'utilisation de 'alarms.setRepeating()'.