2010-05-05 16 views
46

Sto eseguendo un servizio Web che consente agli utenti di registrare i propri viaggi (un po 'come i MyTracks di Google) come parte di un'app più grande. Il fatto è che è facile passare dati al server, inclusi accordi e altri elementi, quando un utente inizia un viaggio o lo termina. Essendo un principiante, non sono sicuro di come impostare un servizio in background che invia gli aggiornamenti di posizione una volta ogni (pre-determinato) periodo (min 3 minuti, max 1 ora) finché l'utente non contrassegna la fine del viaggio, o fino a quando la quantità di tempo prestabilita trascorre.Android: Come inviare periodicamente la posizione a un server

Una volta che il viaggio è iniziato dal telefono, il server risponde con un periodo di polling per il telefono da utilizzare come l'intervallo tra gli aggiornamenti. Questa parte funziona, in quanto posso visualizzare la risposta sul telefono e il mio server registra l'azione dell'utente. Allo stesso modo, il viaggio è chiuso sul lato server dopo la richiesta di chiusura.

Tuttavia, quando ho provato ad avviare un metodo di monitoraggio periodico dall'interno dell'attività StartTrack, utilizzando requestLocationUpdates (provider String, long minTime, float minDistance, listener ListListener) dove minTime è il periodo di polling dal server, non ha funzionato e non ricevo errori. Quindi significa che a questo punto non ho capito, non ho mai usato Android prima.

Ho visto molti posti qui sull'utilizzo di servizi in background con i gestori, intenti in attesa, e altre cose da fare cose simili, ma io davvero non capisco come farlo. Vorrei che l'utente facesse altre cose al telefono mentre gli aggiornamenti sono in corso, quindi se voi ragazzi poteste indicarmi un tutorial che mostra come effettivamente scrivere i servizi in background (forse questi funzionano come classi separate?) O altri modi di facendo questo, sarebbe fantastico.

Grazie!

risposta

15

È necessario creare una classe separata che è una sottoclasse della classe Service.

Service Documentation

L'applicazione principale dovrebbe può chiamare startService e stopService per avviare il processo in background. C'è anche alcune altre chiamate utili nella classe del contesto per la gestione del servizio:

Context Documentation

+0

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

+2

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

+0

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

71

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()'.

+0

Rob, grazie. Ora sto cercando nella documentazione per vedere come usare i filtri intent e come impostare un allarme per chiamare periodicamente il servizio, invece di farlo girare continuamente. – Mark

+0

Mark, ho pubblicato il mio codice - spero che aiuti. –

+0

Grazie ancora Rob. – Mark

2

Sono d'accordo con Rob Kent e, in aggiunta, penso che potrebbe essere più efficace per estendere WakefulBroadcastReceiver nel BroadcastReceiver e utilizzare il suo metodo statico startWakefulService(android.content.Context context,android.content.Intent intent), perché garantisce che il servizio non verrà chiuso da os.

public class YourReceiver extends WakefulBroadcastReceiver { 
    @Override 
    public void onReceive(Context context, Intent intent) { 

     Intent service = new Intent(context, YourService.class); 
     startWakefulService(context, service); 
    } 
} 

Official documentation

Problemi correlati