2016-08-16 10 views
1

Ich versuche Semaphore zu verwenden, um auf meine firebase valueEventListener zu warten. Ich habe eine Benutzer-Info-Aktivität mit 6 verschiedenen Feldern, die der Benutzer ausfüllen muss. Wenn der Benutzer seine Informationen speichert, möchte ich eine Art "alles oder nichts" prüfen. Bestimmte Benutzerinformationen können nicht dupliziert werden ... z. B. Benutzername, E-Mail-Adresse und Telefonnummer. Ich bin mit Feuerbasis und zur Zeit die allgemeine Idee ist das Format:Android: Warten auf Firebase valueEventListener

void saveUserInfo(){ 
    if(field1 exist in database){ 
     return; 
    } 
    . 
    . 
    . 
    if(field6 exist in database){ 
     return; 
    } 

    savefield1(); 
    . 
    . 
    . 
    savefield6(); 
} 

Das Problem, das ich in dem Verfahren habe, ob oder nicht überprüft existiert der Wert bereits in der Datenbank. Hier ist meine aktuelle Methode:

public boolean alreadyInUse(String key, String value) throws InterruptedException { 

    final StringBuilder done = new StringBuilder(""); 
    final Semaphore semaphore = new Semaphore(0); 

    mDatabase.child(key).child(value).addListenerForSingleValueEvent(new ValueEventListener() { 

     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      String result = dataSnapshot.getValue(String.class); 
      if(result == null){ 
       Log.d("WorkoutLog", "result: null"); 
       done.append("false"); 
       semaphore.release(); 
       return; 
      } 
      else if(result.equals(uID)){ 
       Log.d("WorkoutLog", "result: " + result.toString()); 
       done.append("false"); 
       semaphore.release(); 
       return; 
      } 
      else{ 
       Log.d("WorkoutLog", "result: " + result.toString()); 
       done.append("true"); 
       semaphore.release(); 
       return; 
      } 

     } 

     @Override 
     public void onCancelled(DatabaseError databaseError) { 

     } 
    }); 


    semaphore.acquire(); 
    if(done.equals("true")){ 
     return true; 
    } 
    else if(done.equals("false")){ 
     return false; 
    } 
    else{ 
     Log.d("WorkoutLog", "Shouldn't be here"); 
     return true; 
    } 

} 

Im Moment ist die Semaphore nicht frei ... Frage mich, ob jemand kann mir helfen hier. Ohne Semaphor wird die return-Anweisung ausgelöst, bevor die Firebase-Abfrage abgeschlossen werden kann.

+0

Siehe http://stackoverflow.com/questions/33203379/setting-singleton-property-value-in-firebase-listener –

Antwort

5

Die Listener-Callback-Methoden werden im Hauptthread ausgeführt. Wenn Sie alreadyInUse() auf dem Hauptthread aufrufen, wird es den Thread um semaphore.acquire() blockieren, die Rückrufe von dem Ausführen und dem Freigeben des Semaphore verhindern.

Sie könnten this answer zu einer verwandten Frage hilfreich finden.

6

Wie bereits erwähnt, können Sie Firebase Task API auf Google I/O 2016 vorgestellt verwenden.

Task<?>[] tasks = new Task[] { 
    saveUserName(username), 
    saveFriends(friends), 
    saveOtherStuff(stuff) 
}; 

Tasks.whenAll(tasks) 
    .continueWithTask(new RollbackIfFailure()) 
    .addOnCompleteListener(this) 
    .addOnFailureListener(this); 

Wenn jeder Schritt, der eine Modifikation macht parallel ausgeführt werden können, können Sie eine Methode erstellen, die eine Task für jeden von ihnen gibt, wie folgt:

public Task<String> saveUserName(final String username) { 
    final TaskCompletionSource<String> tcs = new TaskCompletionSource<>(); 

    mDatabase.child("users") 
     .child(username) 
     .addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      String username = dataSnapshot.getValue(String.class); 
      if (null == username) { 
       tcs.setResult(null); 
      } else { 
       tcs.setException(new IllegalStateException(username)); 
      } 
     } 

     @Override 
     public void onCancelled(FirebaseError firebaseError) { 
      tcs.setException(new IOException(TAG, firebaseError.toException())); 
     } 
    }); 

    return tcs.getTask(); 
} 

Wenn einer von ihnen ausfällt, Sie müssen die Operationen zurücksetzen. Da dies durch eine einzige Aufgabe threated werden, können Sie eine Continuation Aufgabe erstellen:

class RollbackIfFailure implements Continuation<Void, Task<Void>> { 
    @Override 
    public Task<Void> then(@NonNull Task<Void> task) throws Exception { 

     final TaskCompletionSource<Void> tcs = new TaskCompletionSource<>(); 

     if (task.isSuccessful()) { 
      tcs.setResult(null); 
     } else { 
      // Rollback everything 
     } 

     return tcs.getTask(); 
    } 
} 
Verwandte Themen