2017-03-03 7 views
1

Ich verwende Java8. Ich habe einen Listener, der onSuccess aufruft, wenn er mit einem customToken abgeschlossen wird.Java Event Listener Rückgabewert

@Override 
public String getCustomToken(Person person) { 
    FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(new OnSuccessListener<String>() { 
     @Override 
     public void onSuccess(String customToken) { 
      // I would like to return the customToken 
     } 
    }); 
    return null; 
} 

Frage

Wie bekomme ich diese Methode den String customToken zurückzukehren?

+0

Nein, wenn ich 'onSuccess' Rückgabeobjekt in' String' ändere, erhält es einen Kompilierfehler: 'Der Rückgabetyp ist nicht kompatibel mit OnSuccessListener – Richard

+0

Endgültige Variable zu einem Containerobjekt außerhalb des Aufrufs bereitstellen aber innerhalb der Methode, lassen Sie den Listener das Token in diesen Container schreiben, dann herausholen und zurückgeben. – Thomas

Antwort

2

Dies würde funktionieren syntaktisch:

final List<String> tokenContainer = new ArrayList<>(); 
FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(new OnSuccessListener<String>() { 
    @Override 
    public void onSuccess(String customToken) { 
     tokenContainer.add(customToken); 
    } 
}); 
return tokenContainer.get(0); 

Wie gesagt; Das funktioniert syntaktisch. Aber wenn es wirklich funktioniert, würde abhängen, ob der gesamte Fluss in einem Thread passiert; oder mehrere.

Mit anderen Worten: Wenn der obige Code in Sequenz ausgeführt wird, dann sollte diese Liste enthält genau ein Eintrag am Ende. Aber wenn dieser Rückruf auf einem anderen Thread stattfindet, benötigen Sie eine kompliziertere Lösung. haben die „äußere Sache“ sitzen und warten, bis der Rückruf passieren: Ein hackish Weise könnte

return tokenContainer.get(0); 

mit

while (tokenContainer.isEmpty()) { 
    Thread.sleep(50); 
} 
return tokenContainer.get(0); 

Mit anderen Worten vorangestellt sein. Aber der vernünftigere Ansatz wäre, stattdessen ein Feld der umgebenden Klasse zu verwenden.

Edit: wenn das oben genannte ein hack oder nicht; könnte zu einem gewissen Grad von deinem Kontext abhängen. Das einzige, was wirklich Probleme mit Ihrem Code ist die Tatsache, dass Sie einen neuen Listener erstellen; was "irgendwo" hinzugefügt wird ... um dort zu bleiben ?! Was ich meine ist: sollte nicht Code diesen Hörer irgendwo sein?

+0

Ich habe einen Thread (aus einem RESTful Service), der diese Methode aufruft. Es erwartet eine Antwort von String 'customToken'. Ich denke also nicht, dass die obige Lösung mein Problem behebt. – Richard

+0

Ich sehe deine Lösung mit dem 'Schlaf' oben jetzt, danke. – Richard

+0

Das Warten auf den 'tokenContainer' wird funktionieren. Danke für deine Hilfe – Richard

0

Extrakt eine Variable in einen geeigneten Rahmen (Klasse oder Attribut Verfahren variable)

private String customToken; 

@Override 
public String getCustomToken(Person person) { 
    FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(new OnSuccessListener<String>() { 
     @Override 
     public void onSuccess(String customToken) { 
      this.customToken = customToken 
     } 
    }); 
    return null; 
} 
+0

Verwenden Sie keine Instanzvariablen hier. Wenn Sie die Methode parallel zu verschiedenen Personen aufrufen, verlieren Sie höchstwahrscheinlich einen der Token. – Thomas

+0

Danke, das sieht fast aus, was ich brauche. Aber ich habe einen RESTful-Service, der diese Methode aufruft, die das 'customToken' benötigt. Es ist also in Ordnung, die Membervariable zu setzen, aber wie kann ich auf die Anfrage antworten? – Richard

1

Ihre Frage ist faszinierend, aber die angenommene Antwort liefert Ihnen leider falsche Mittel.

Das Problem mit Ihrer Frage ist das der API. Sie versuchen, Rückrufe auf eine Weise zu verwenden, für die sie nicht vorgesehen sind. Ein Callback soll per Definition ein Mittel bieten, etwas asynchron zu tun. Es ist mehr wie eine Spezifikation dessen, was zu tun ist, wenn etwas passiert (in Zukunft). Eine synchrone Methode wie getCustomToken(), die etwas zurückgibt, das ein Ergebnis einer inhärent asynchronen Operation wie onSuccess() ist, impliziert eine grundlegende Trennung.

Während Rückrufe zu behandeln, ist es wichtig zu verstehen, die Bedeutung von Fortsetzungen: Maßnahmen ergreifen, wenn bestimmte Ereignisse von Interesse passieren. Beachten Sie, dass diese Ereignisse möglicherweise nicht einmal auftreten. Sie geben jedoch im Code die Aktionen an, die ausgeführt werden sollen, wenn diese Ereignisse auftreten. Daher ist der Fortsetzungsstil eine Verschiebung vom prozeduralen Stil.

Was zur Komplexität des Datenflusses beiträgt, ist die Syntax der anonymen inneren Klassen. Du tendierst zu denken: "Oh, warum kann ich nicht einfach von hier zurückkehren, was onSuccess() zurückgibt?Immerhin ist der Code richtig hier. "Aber stellen Sie sich vor, dass Java keine inneren Klassen hatte (und wie Sie vielleicht wissen, kann (anonyme) innere Klasse leicht durch eine Klasse ersetzt werden, die keine innere Klasse ist) gebraucht haben, wie etwas zu tun ist:

OnSuccessListener listener = new SomeImplementation(); 
FirebaseAuth.getInstance().createCustomToken(listener); 

Nun wird der Code, der Daten (String) zurückgegeben gegangen Sie können sogar visuell Grund dafür, dass in diesem Fall zurück gibt es keine Möglichkeit für Ihre Methode eine Zeichenfolge ist. - Es ist einfach nicht da!

Also, ich ermutige Sie zu überlegen, was passieren soll, wenn und wann (in Zukunft) onSuccess() auf deraufgerufen wird Wenn Sie also in Ihre API, die getCustomToken() API-Methode (diese gibt eine Token-Zeichenfolge, eine Person-Instanz vorausgesetzt) ​​geben möchten.

Wenn Sie unbedingt ein solches Verfahren bereitzustellen, müssen Sie

  • Sollte dokumentieren, dass das zurückgegebene Token null (oder etwas Sinnvolles wie None) sein kann, und dass Ihre Kunden wieder versuchen müssen, wenn sie eine gültige wollen Wert.

  • Sollte einen Listener bereitstellen, der einen Thread-sicheren Container mit Tokens aktualisiert, die von dieser Methode gelesen werden.

Googeln herum, fand ich die Firebase documentation. Dies scheint auch nimmt eine Aktion auf Erfolg vorschlagen (in Fortsetzung Stil):

FirebaseAuth.getInstance().createCustomToken(uid) 
    .addOnSuccessListener(new OnSuccessListener<String>() { 
     @Override 
     public void onSuccess(String customToken) { 
      // **Send token back to client** 
     } 
    }); 

Das andere Problem mit dem Versuch, eine solche API zur Verfügung zu stellen die scheinbare Komplexität des Codes für etwas trivial ist. Der Datenfluss ist ziemlich komplex und schwer zu verstehen.

Wenn Blockierung Ihnen als Lösung akzeptabel ist, dann vielleicht können Sie den Callable-Future-Stil verwenden, in dem Sie eine Callable passieren und dann später eine get() auf dem Future tun, die blockieren können. Aber ich bin mir nicht sicher, ob das hier eine gute Designwahl ist.