Recentemente ho scritto uno di questi e ha deciso che non è una buona idea lasciare un servizio in background in esecuzione. Probabilmente sarà comunque spento dal sistema operativo, o potrebbe esserlo. Quello che ho fatto è stato utilizzare un filtro per l'intenzione di avvio e quindi impostare un allarme utilizzando la gestione degli allarmi in modo che la mia app fosse riavviata a intervalli regolari, e quindi ha inviato i dati. Puoi trovare buone informazioni sui servizi e il gestore degli allarmi nella documentazione di Android.
Per prima cosa ho creato un ricevitore di trasmissione che avvia semplicemente il mio servizio quando viene aperta una connessione Internet (sono interessato solo se c'è una connessione - si potrebbe desiderare di filtrare anche per l'evento di avvio). Il ricevitore lancio deve essere di breve durata, quindi basta avviare il servizio:
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);
}
}
Nel manifesto ho:
<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>
Notate come ho un filtro per il mio allarme, che è ciò che permette io per chiudere il servizio e riavviarlo dopo aver fatto il suo lavoro.
La parte superiore del mio servizio del monitor appare come:
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 avvia un'AsyncTask, che è probabilmente me essere eccessivamente prudente (questo era solo il mio terzo app Android). L'AsyncTask afferra i dati GPS, lo invia ad internet e, infine, imposta l'allarme successivo:
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;
}
}
mi accorgo che non sto chiamando stopSelf() dopo aver impostato l'allarme, quindi il mio servizio si siederà intorno a non fare nulla a meno che non spento dal sistema operativo. Dato che sono l'unico utente di questa app, non importa, ma per un'app pubblica, l'idea è di impostare l'allarme per l'intervallo successivo, quindi stopSelf per chiudere.
Aggiornamento Vedere il commento di @juozas sull'utilizzo di 'alarms.setRepeating()'.
Grazie un milione. Sono stato in grado di creare il servizio, avviarlo e fermarlo. Ora devo assicurarmi che il sistema non lo uccida e che venga chiamato come necessario. – Mark
Ricordare che il sistema operativo interromperà periodicamente (e riavvia) il servizio come meglio crede: http://developer.android.com/reference/android/app/Service.html#ProcessLifecycle – jscharf
jscharf, come faccio a garantire che il servizio è garantito per essere chiamato periodicamente e viene eseguito solo fino a quando l'utente non esegue una determinata azione per interrompere le chiamate? Sto cercando di usare un allarme e passare attraverso i documenti su come usarli ... – Mark