2016-10-11 1 views
1

Ich schreibe eine Methode, mit der ich eine Datenbank abfragen und eine andere Methode ausführen möchte, nachdem die Abfrage beendet wurde. Da der Aufruf der Datenbank asynchron ist, aber ich kann nicht scheinen, um herauszufinden, wie die Hauptströmung der Ausführung zu stoppen, bis der Datenbank-Aufruf abgeschlossen ist, etwa so:Asynchroner Ausführungsablauf in Java

public void getUserFromEmail(String email) { 
    Firebase usersRef = rootRef.child("users"); 

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
     } 

     @Override 
     public void onCancelled(FirebaseError firebaseError) { 
      System.out.println(firebaseError.getMessage()); 
     } 
    }); 
    //Perform some more operations after database call is finished 
} 

Ich bin es gewohnt, zu arbeiten mit asynchronen Callbacks in Nodejs, aber ich kann nicht herausfinden, ob es eine gleichwertige Möglichkeit gibt, den Ausführungsfluss in Java zu steuern. Gibt es einen einfachen Weg, um das zu erreichen, was ich tun möchte?

Antwort

1

Sie können den Hauptfluss nicht stoppen, der Hauptthread wird fortgesetzt. (Es sei denn, Sie stoppen es sonst, aber das führt dazu, dass "Anwendung nicht reagiert", also nicht).

Tun Sie einfach die Aktion im Rückruf.

public void getUserFromEmail(String email) { 
    Firebase usersRef = rootRef.child("users"); 

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      // Perform some more operations after database call is finished 
      // OR... callSomeMethod(); 
     } 

     @Override 
     public void onCancelled(FirebaseError firebaseError) { 
      System.out.println(firebaseError.getMessage()); 
     } 
    }); 

} 

public void callSomeMethod() { 
    // Perform some more operations after database call is finished 
} 

Wenn Sie von einem Knoten Hintergrund kommen, dann sieht das vielleicht ähnlich. Vorbei an der Funktion zu getUserFromEmail

public void otherMethod() { 
    getUserFromEmail("[email protected]", new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      // Perform some more operations after database call is finished 
     } 

     @Override 
     public void onCancelled(FirebaseError firebaseError) { 
      System.out.println(firebaseError.getMessage()); 
     } 
    }); 
} 

public void getUserFromEmail(String email, ValueEventListener listener) { 
    rootRef.child("users") 
     .orderByKey() 
     .equalTo(email) 
     .addListenerForSingleValueEvent(listener); 
} 
+0

Was passiert, wenn ich einen Wert aus dem ursprünglichen meyhod einmal der Rückruf erfolgt zurückkehren wollte? – Matt

+1

Die Methode ist "ungültig". Sie können nicht zurückkehren. Du solltest auch nicht wirklich. Async spielt nicht gut mit der Anweisung 'return' –

+1

Sie können die Benutzeroberfläche direkt aus den Daten aktualisieren, die Sie aus 'dataSnapshot' innerhalb des Callbacks erhalten. –

0

können Sie den Fluss steuern, wenn Sie wirklich mit Sperren wollen. Zum Beispiel mit CountdownLatch:

public DataSnapshot getUserFromEmail(String email) throws InterruptedException { 
    AtomicReference<DataSnapshot> dataSnapshotRef = new AtomicReference<DataSnapshot>(); 
    CountDownLatch completeSignal = new CountDownLatch(1); 

    Firebase usersRef = rootRef.child("users"); 

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      dataSnapshotRef.set(dataSnapshot); 
      // Causes execution of the waiting thread to continue 
      // from the `await` call 
      completeSignal.countDown(); 
     } 

     @Override 
     public void onCancelled(FirebaseError firebaseError) { 
      completeSignal.countDown(); 
     } 
    }); 

    // Execution on this thread pauses at the `await` call until 
    // countDown has been called from another thread. 
    completeSignal.await(); 

    // Do stuff after onDataChanged or onCancelled has been called 
    DataSnapshot dataSnapshot = dataSnapshotRef.get(); 
} 

Allerdings wird dieser Ansatz den Thread blockieren, die getUserFromEmail ausgeführt, bis Firebase einige Daten zurückgegeben hat. Insbesondere, wenn Sie getUserFromEmail von Ihrem UI-Thread aufrufen, wird dies den UI-Thread blockieren und einen ANR verursachen.

Es ist besser, dem Callback-Ansatz wie so folgen:

public DataSnapshot getUserFromEmail(String email, ValueEventListener listener) { 
    Firebase usersRef = rootRef.child("users"); 

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(listener); 
} 

public void getUserAndThenDoSomething() { 
    getUserFromEmail("[email protected]", new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      // Do stuff after getting the email here 
     } 

     @Override 
     public void onCancelled(FirebaseError firebaseError) { 
     } 
    }); 
} 
+0

Das letzte Mal, als ich das zu ANRs überprüft habe. Sehen Sie mein Update in http://stackoverflow.com/a/33204705/209103 –

+0

Wenn Sie es im UI-Thread ausführen, dann ja, sonst nicht. Der Antwort hinzugefügt, um Benutzer über die Verwendung dieser Methode aus dem UI-Thread zu warnen. –