2010-05-05 13 views
46

Ich betreibe einen Webdienst, der es Benutzern ermöglicht, ihre Reisen (so wie Google MyTracks) als Teil einer größeren App aufzuzeichnen. Die Sache ist, dass es einfach ist, Daten, einschließlich Koordinaten und andere Gegenstände, an den Server weiterzugeben, wenn ein Benutzer eine Reise beginnt oder beendet. Da ich ein Neuling bin, bin ich mir nicht sicher, wie ich einen Hintergrunddienst einrichten soll, der die Standortaktualisierungen einmal in jedem (vorbestimmten) Zeitraum (min. 3 Minuten, max. 1 Std.) Sendet, bis der Benutzer das Ende der Reise markiert, oder bis a voreingestellte Zeit verstreicht.Android: So senden Sie regelmäßig den Standort an einen Server

Sobald die Reise vom Telefon aus gestartet wurde, antwortet der Server mit einer Abrufperiode, die das Telefon als Intervall zwischen den Aktualisierungen verwenden kann. Dieser Teil funktioniert, indem ich die Antwort auf dem Telefon anzeigen kann, und mein Server registriert die Aktion des Benutzers. In ähnlicher Weise wird die Fahrt serverseitig bei der Anforderung für eine enge Fahrt geschlossen.

aber als ich versuchte, eine periodische Tracking-Methode ausgehend von der Innenseite des Starttrack Aktivität requestLocationUpdates (String-Anbieter, lange MINTIME, float MinDistance, Location Hörer), wo MINTIME ist die Umfrage Zeitraum vom Server verwendet, es funktioniert einfach nicht und ich bekomme keine Fehler. Es bedeutet also, dass ich zu diesem Zeitpunkt keine Ahnung habe und vorher noch nie Android benutzt habe.

Ich habe viele Beiträge hier über die Verwendung von Hintergrunddiensten mit Handlern, ausstehende Absichten und andere Dinge gesehen, um ähnliche Sachen zu tun, aber ich verstehe wirklich nicht, wie man es macht. Ich möchte, dass der Benutzer während des Updates andere Dinge am Telefon erledigt. Wenn ihr also auf ein Tutorial zeigen könnt, das zeigt, wie man Hintergrunddienste schreibt (vielleicht laufen diese als separate Klassen?) Oder andere Möglichkeiten dabei wäre das großartig.

Danke!

Antwort

15

Sie müssen eine separate Klasse erstellen, die eine Unterklasse der Klasse Service ist.

Service Documentation

Ihre primäre Anwendung sollte kann startService und stopService rufen Sie den Hintergrundprozess zu starten. Theres auch einige andere nützliche Anrufe im Zusammenhang mit Klasse, den Dienst zu verwalten:

Context Documentation

+0

Dank einer Million. Ich konnte den Service aufbauen, starten und stoppen. Ich muss jetzt sicherstellen, dass das System es nicht tötet und dass es bei Bedarf aufgerufen wird. – Mark

+2

Denken Sie daran, dass das Betriebssystem Ihren Dienst regelmäßig stoppt (und neu startet), wie er es für richtig hält: http://developer.android.com/reference/android/app/Service.html#ProcessLifecycle – jscharf

+0

jscharf, wie kann ich das sicherstellen? Der Service wird garantiert regelmäßig aufgerufen und nur dann ausgeführt, bis der Benutzer eine bestimmte Aktion ausführt, um die Anrufe zu beenden? Ich suche einen Alarm und gehe durch die Dokumentation, wie man sie benutzt ... – Mark

71

Ich schrieb vor kurzem eine davon und beschlossen, es keine gute Idee ist, einen Hintergrunddienst laufen zu lassen. Es wird wahrscheinlich sowieso vom Betriebssystem heruntergefahren, oder es könnte sein. Was ich getan habe, war einen Filter für den Boot-Intent zu verwenden und dann einen Alarm über den Alarm-Manager zu setzen, so dass meine App in regelmäßigen Abständen neu gestartet wurde, und dann wurden die Daten gesendet. In der Android-Dokumentation finden Sie gute Informationen zu Diensten und zum Alarmmanager.

Zuerst habe ich einen Broadcast-Empfänger erstellt, der einfach meinen Dienst startet, wenn eine Internetverbindung geöffnet wird (ich bin nur interessiert, wenn eine Verbindung besteht - Sie könnten auch nach dem Boot-Ereignis filtern). Der Start-Empfänger muss von kurzer Dauer sein, so starten Sie einfach Ihren 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); 
    } 
} 

Im Manifest habe ich:

<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> 

Beachten Sie, wie ich einen Filter für meine eigenen Alarm haben, das, was ist erlaubt ich, um den Dienst zu schließen und es neu zu starten, nachdem es seine Arbeit getan hat.

Die Spitze von meinem Monitor-Dienst wie folgt aussieht:

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 eine AsyncTask beginnt, was wahrscheinlich ist, ich übermäßig vorsichtig zu sein (das war nur mein dritte Android-App). Die AsyncTask die GPS-Daten packt, sendet sie an das Internet und setzt schließlich den nächsten Alarm:

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; 
    } 
} 

merke ich, dass ich nicht stopSelf bin() aufgerufen, nachdem der Alarm ausgelöst, so wird mein Service herumsitzen nichts zu tun, es sei denn Herunterfahren von der op sys. Da ich der einzige Benutzer dieser App bin, ist das egal, aber für eine öffentliche App, die Idee ist, dass Sie den Alarm für das nächste Intervall dann stopSelf zum Schließen einstellen.

Aktualisieren Siehe den Kommentar von @juozas über die Verwendung von 'alarms.setRepeating()'.

+0

Rob, danke. Ich suche jetzt in der Dokumentation, um zu sehen, wie die Intent-Filter verwendet werden und wie ein Alarm eingerichtet wird, um den Dienst regelmäßig aufzurufen, anstatt ihn ständig ausführen zu lassen. – Mark

+0

Mark, ich habe meinen Code veröffentlicht - hoffe, es hilft. –

+0

Danke nochmal Rob. – Mark

2

Ich stimme mit Rob Kent überein, und in zusätzlichen könnte ich denken, könnte besser sein, WakefulBroadcastReceiver in Ihrem BroadcastReceiver verlängert und verwenden Sie es statische Methode startWakefulService(android.content.Context context,android.content.Intent intent), weil es garantiert, dass Ihr Dienst nicht von os geschlossen wird.

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

Verwandte Themen