2016-10-06 4 views
1

Mein ZielFirebase onComplete nie im Hintergrund Prozess namens

eine App zu entwickeln, die Benutzer-Ortung auf & auszuschalten ermöglicht. Wenn das Tracking aktiviert ist, möchte ich die Standortdaten des Benutzers an Firebase senden.

Meine versuchte Lösung

Ich benutze PendingIntent und die Google-Dienste FusedLocationAPI Spielen Lage im Hintergrund zu verfolgen. Bei jeder Standortaktualisierung (unterbrochen von 30 Sekunden) löst der PendingIntent eine Absicht (mit einer Standortaktualisierung, falls verfügbar) auf einen WakefulBroadcastReceiver aus. Der WakefulBroadcastReceiver sollte dann die Standortdaten in Firebase speichern.

Hier ist, was die WakefulBroadcastReceiver wie folgt aussehen: (! Auf einigen Geräten)

public class LocationProcessingReceiver extends WakefulBroadcastReceiver { 

    private static final String TAG = "LocationProcessingRcv"; 

    @Override 
    public void onReceive(Context context, Intent intent) { 
     if (LocationResult.hasResult(intent)) { 
      Log.i(this.TAG, "Received location result"); 

      // Extract location from result and push to server 
      Location location = LocationResult.extractResult(intent).getLastLocation(); 
      this.pushLocationToServer(location, intent.getStringExtra("userId")); 
     } 
     else { 
      Log.i(TAG, "No location result"); 
     } 
    } 

    /** 
    * Pushes location to Firebase server 
    * @param location 
    */ 
    private void pushLocationToServer(Location location, String userId) { 

     FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(); 

     // Data to update the latest status of the user 
     Map<String, Object> statusMap = new HashMap<String, Object>(); 
     // ... various statusMap.put(...) calls go here 

     // Get reference to Firebase as well as key at which to update reference 
     DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference(); 

     dbRef.child(DB_LOC_HISTORY_REFERENCE).push().updateChildren(historyMap, new DatabaseReference.CompletionListener() { 
      @Override 
      public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) { 
       if (databaseError == null) { 
        Log.i(TAG, "Successfully pushed to server"); 
       } 
       else { 
        Log.i(TAG, "Server push failed: " + databaseError.toString()); 
       } 
      } 
     }); 
    } 
} 

Das Problem

Bei einigen Telefonen wird der onComplete Rückruf nie genannt. Ich habe keine Ahnung, warum das nicht funktioniert ... Auf einigen Geräten funktioniert der Rückruf wie erwartet. Bei anderen wird der Rückruf nie empfangen und ich sehe die Daten nie in Firebase aktualisiert.

Die Protokolle zeigen, dass die Standortaktualisierungen einwandfrei funktionieren und dass die onReceive() - Methode des WakefulBroadcastReceivers wie erwartet regelmäßig aufgerufen wird.

Ich hätte erwartet onComplete, aber um einen Fehler anzuzeigen, wenn ein Verbindungsproblem vorliegt. Aber das passiert nie, und ich kämpfe darum, warum.

Ein Vorbehalt: Viele dieser Geräte laufen auf 3G. Ist es möglich, dass, wenn meine App im Hintergrund ist, der BroadcastReceiver beendet wird, bevor Firebase das erledigt, vielleicht weil die 3G-Verbindung zu langsam ist?

+0

Haben Sie jemals eine Lösung gefunden? – andygeers

Antwort

1

Das Problem kann mit dem Lebenszyklus von BroadcastReceiver zusammenhängen.

Die Dokumentation erklärt, dass:

A BroadcastReceiver Objekt für die Dauer des Anrufs ist nur gültig zu OnReceive (Kontext, Intent). Sobald der Code von dieser -Funktion zurückgegeben wird, betrachtet das System das Objekt als beendet und nicht mehr als aktiv.

Dies hat wichtige Auswirkungen auf das, was Sie in einem OnReceive (Context, Intent) Umsetzung tun können: alles, was asynchronen Betrieb erfordert nicht verfügbar, weil Sie Rückkehr aus der Funktion benötigt, die asynchron zu handhaben Betrieb, aber bei dieser Punkt der BroadcastReceiver ist nicht mehr aktiv und damit das System ist frei, um seinen Prozess vor dem asynchronen abgeschlossen.

Da die FirebaseDatabase Operationen wird das System, in manchen Fällen zerstören Ihre LocationProcessingReceiver Instanz vor dem DB-Betrieb durchgeführt wird asynchron auf einem Hintergrund-Thread ausgeführt werden.

Sie könnten denken, dass die Verwendung eines WakefulBroadcastReceiver verhindert, dass das Empfängerobjekt vorzeitig zerstört wird, aber das ist nicht der Fall. WakefulBroadcastReceiver ist für den sicheren Start eines Dienstes gedacht, den Sie hier nicht ausführen. Es bringt keinen Nutzen. Verwenden Sie stattdessen eine BroadcastReceiver.

Um sicherzustellen, dass der DB-Vorgang abgeschlossen werden kann, muss ein Wakelock in (möglicherweise mit einem Timeout für Sicherheit) und dann im Callback freigegeben werden.

+0

Danke für die Info. Gibt es eine Möglichkeit, den BroadcastReceiver am Leben zu erhalten, bis Firebase seinen Job beendet? Oder generell * irgendeinen * Weg, um sicherzustellen, dass die FirebaseDatabase-Operationen nicht unterbrochen werden? –

+0

Ich habe meinen ursprünglichen Beitrag bearbeitet, um einen Vorschlag zur Verwendung eines Wakelocks hinzuzufügen. –

+0

Danke! Ich versuche es mal. Es scheint ein wenig hacky obwohl ... ist es besser, einen Dienst mit einer Firebase Verbindung zu haben, die immer im Hintergrund läuft, und damit mein BroadcastReceiver einfach "pushToServer" auf dieser Referenz nennt? Ich bin mir nicht sicher, wie ich das umsetzen würde, aber was machen Sie konzeptionell? –

Verwandte Themen