2017-11-10 3 views
0

Ich habe eine GUI zu meiner Client-Anwendung hinzugefügt und versucht, die Socket-Nachrichten an sie weiterzuleiten. Ich verspottete den Client, um ein Singleton bis jetzt zu sein, , so dass ich es von der Application Thread, zugreifen konnte, aber jetzt möchte ich mehrere Clients auf ihre GUI zugreifen. Ich weiß fxml hat Eigenschaft Listener aber gebunden Ich will es so einfach wie möglich. Um direkt mit den Threads zu arbeiten. Wie würde ich machen Platform.runLater() Referenz Szene Instanz, wenn Application.launch() Konstrukte eigene? Ich habe ziemlich ausführlich gelesen, aber Lösungen überall entpuppen sich, eine fxml Schnittstelle zu hebeln, die die Arbeit überspringt, die ich nicht mögen würde. Control-Klasse (vereinfacht, die Ausgabe auf der Leitung 9):Ändern JavaFX-Anwendungsszene aus anderen Thread?

public class Client { 
    private Socket socket = new Socket("127.0.0.1",4444); 
    private BufferedReader incoming = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
    private Browser browser = new Browser(); 
    public static void main(String[] arguments) throws IOException { 
     Application.launch(browser.class); 
     while(socket.isConnected()) { 
      Platform.runLater(new Runnable(){ 
       @Override public void run(){ 
        try{browser.bubble(incoming.readLine());} 
        catch(IOException fail){fail.printStackTrace();} 
       } 
      }); 
     } 
    } 
} 

Benutzeroberfläche (vereinfacht):

public class Browser extends Application { 
    private Stage stage; 
    private Scene scene; 
    private BorderPane layout = new BorderPane(); 
    private VBox history = new VBox(); 
    @Override public void start(Stage stage) throws Exception { 
     layout.setCenter(history); 
     scene = new Scene(layout); 
     stage = stage; 
     stage.setScene(scene); 
     stage.show(); 
    } 
    public void bubble(String message){ 
     VBox record = new VBox(); 
     Label label = new Label(message); 
     record.getChildren().add(label); 
     history.getChildren().add(record); 
    } 
} 
+1

In der überwiegenden Mehrheit der Fälle sollten Sie 'Application.launch()' nicht von irgendwo aufrufen, außer einer Hauptmethode, und sollten nichts anderes in der Hauptmethode tun. Mach das anders herum; I.e. Erstellen Sie Ihre 'Client'-Instanz von Ihrer' start() 'Methode und rufen Sie die Methode auf, die den von Ihnen geposteten Code enthält (in einem Hintergrundthread). Sie haben weiterhin Threading-Probleme - Sie blockieren den fx-Anwendungsthread mit dem 'readLine()' -Aufruf. –

+0

Ja, tut mir leid, ich habe vergessen, die psvm-Methode zu integrieren, korrigiert. Ich mag die Prinzipien, die Du zeichnest, nichts anderes in der Hauptmethode und instanziierst von der Anwendung ... Das Versenden dieser Instanz an einen dritten Thread könnte das Blockierungsproblem ebenso lösen, könnte es nicht ... Ich werde wahrscheinlich auf diese Weise gehen. – bpstrngr

Antwort

2

Sie sollten immer nur eine Instanz Ihrer Application Unterklasse haben, die die Instanz erstellt wird, wenn Die Anwendung wird über Application.launch() gestartet. Ihre main() Methode in einer JavaFX-Anwendung sollte eigentlich nur Application.launch() aufrufen und sonst nichts tun; Sie sollten die start() (oder init()) Methode (n), die als Teil des Startvorgangs aufgerufen werden, als Einstiegspunkt für die Anwendung betrachten.

Konsequenterweise sollten Sie die Client Instanz aus der start()-Methode erstellen und in einem Hintergrundthread festlegen, was sie tut.

Sie all dies durch Refactoring Code erreichen können wie folgt:

public class Browser extends Application { 
    private Stage stage; 
    private Scene scene; 
    private BorderPane layout = new BorderPane(); 
    private VBox history = new VBox(); 
    @Override public void start(Stage stage) throws Exception { 
     layout.setCenter(history); 
     scene = new Scene(layout); 
     stage = stage; 
     stage.setScene(scene); 
     stage.show(); 

     Client client = new Client(this); 
     Thread thread = new Thread(client::processIncomingData); 
     thread.setDaemon(true); 
     thread.start(); 
    } 
    public void bubble(String message){ 
     VBox record = new VBox(); 
     Label label = new Label(message); 
     record.getChildren().add(label); 
     history.getChildren().add(record); 
    } 
    public static void main(String[] args) { 
     Application.launch(args); 
    } 
} 

und

public class Client { 
    private Socket socket = new Socket("127.0.0.1",4444); 
    private BufferedReader incoming = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
    private Browser browser ; 

    public Client(Browser browser) { 
     this.browser = browser ; 
    } 

    public void processIncomingData() { 
     while(socket.isConnected()) { 
      try { 
       String data = incoming.readLine(); 
       Platform.runLater(() -> browser.bubble(data)); 
      } catch (IOException exc) { 
       // TODO: handle properly 
       exc.printStackTrace(); 
      } 
     } 
    } 
} 

Ein paar andere Dinge zu beachten: Application.launch() blockiert, bis die Anwendung beendet wird; also in Ihrem ursprünglichen Code würde Ihre while Schleife nicht einmal starten, bis die Anwendung geschlossen wurde. Auch die readLine()-Methode blockiert, sodass Sie dies nicht auf dem FX-Anwendungs-Thread ausführen möchten (dies verhindert, dass die Benutzeroberfläche in irgendeiner Weise reagiert, bis eine Zeile gelesen wird). Das letztere Problem wird behoben, indem readLine() aus dem Platform.runLater() Block verschoben wird.

+0

Okay, also habe ich die Logik umgekehrt und aus dem Application-Thread instanziiert. Es ist nett genug. Das Einfügen von 'Application.launch()' vor der Schleife war nur ein Fehler in meiner Verkürzung, aber es war großartig zu erwähnen. Danke, James. – bpstrngr

Verwandte Themen