JavaFX + Java + Task +Thread. Как организовать многозадачность?
Пишу клиент серверное приложение используя Socket, ServerSocket. В качестве графики использую javaFx. Чтобы отображать сообщения всех пользователей в 'TextArea printMessageField' создал в классе Client отдельный поток (Task + Thread) для получения сообщений от сервера. В чем заключается проблема:
- Если не использовать поток, то после нажатия на кнопку "Войти в чат", программа начинает не отвечать и закрывается. Все из-за цикла while (true), что служит для получения сообщений от сервера.
- С добавлением потока стало все лучше, но не полностью. Теперь после нажатия на кнопку "Войти в чат" программа не ломается. Далее, если выйти из чата (нажать кнопку "Выйти из чата") и попробовать обратно зайти, то программа виснет. (Вас перекинет в окно авторизации, я вырежу код, что отвечает за это. Необходимо нажать только на кнопку "Войти").
- С добавлением потока также после закрытия окна (нажать на крестик справа сверху окна) IntelliJ показывает, что программа все еще работает. (Убрал все проверки после нажатия на кнопку "Войти", программа перестала виснуть, но после закрытия все равно IntelliJ показывает, что программа работает).
Вопрос: как это исправить?
Main.java
package app;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("view/chatWindow.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 800, 600);
stage.setScene(scene);
stage.show();
}
}
Controller.java
package app.controllers;
//Соединение с сервером
import java.io.*;
import java.net.Socket;
//Графика
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class Controller {
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private Label errorText;
@FXML
private Button authorizationButton;
@FXML
private TextField login_field;
@FXML
private PasswordField password_field;
@FXML
private Button registrationButton;
@FXML
void initialize() {
//Действия для кнопки входа
authorizationButton.setOnAction(actionEvent -> {
authorizationButton.getScene().getWindow().hide();
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/app/view/chatWindow.fxml"));
try {
fxmlLoader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
Parent root = fxmlLoader.getRoot();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
});
}
}
ChatController.java
package app.controllers;
import java.io.*;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class ChatController {
private Client client;
//Показывет есть ли подключение или нет, чтобы другие кнопки не работали
private static boolean statusConnect;
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private Button enterChatButton;
@FXML
private Button exitChatButton;
@FXML
private TextField inputMessageField;
@FXML
private Button menuFuncButton;
@FXML
private static TextArea printMessageField;
@FXML
private static TextArea printUsersField;
@FXML
private Button sendFileButton;
@FXML
private Button sendMessageButton;
@FXML
void initialize() {
//Кнопка входа
enterChatButton.setOnAction(actionEvent -> { //сделать потеряно соединение вдруг что
client = new Client();
statusConnect = true;
});
//Кнопка выхода из чата
exitChatButton.setOnAction(actionEvent -> {
if (statusConnect){
statusConnect = false;
//Закрываем поток
try {
client.exitChat();
} catch (IOException e) {
throw new RuntimeException(e);
}
//Смена окна
exitChatButton.getScene().getWindow().hide();
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/app/view/app.fxml"));
try {
fxmlLoader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
Parent root = fxmlLoader.getRoot();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
} else {
System.out.println(1);
}
});
}
}
Client.java
package app.controllers;
import javafx.concurrent.Task;
import java.io.*;
import java.net.Socket;
public class Client {
private boolean status;
private Socket socket;
private BufferedReader reader;
private BufferedWriter writer;
Client () {
new Thread(task).start();
}
Task<Void> task = new Task<Void>() {
@Override
public Void call() throws Exception {
try {
socket = new Socket("127.0.0.1", 1821);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
status = true;
while (status){
//тут клиент должен получать сообщения от сервера
reader.readLine();
}
socket.close();
writer.close();
reader.close();
} catch (IOException e) {
//Тут добавлял return null не помогло
e.printStackTrace();
}
return null;
}
};
public void exitChat() throws IOException {
status = false;
}
}
App.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" style="-fx-background-color: #ffca77;" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.controllers.Controller">
<children>
<Label layoutX="341.0" layoutY="165.0" text="Авторизация">
<font>
<Font size="24.0" />
</font>
</Label>
<TextField fx:id="login_field" layoutX="326.0" layoutY="222.0" promptText="Логин" style="-fx-background-color: #ffca77;">
<font>
<Font size="14.0" />
</font>
</TextField>
<PasswordField fx:id="password_field" layoutX="326.0" layoutY="270.0" promptText="Пароль" style="-fx-background-color: #ffca77;">
<font>
<Font size="14.0" />
</font>
</PasswordField>
<Button fx:id="authorizationButton" layoutX="326.0" layoutY="329.0" mnemonicParsing="false" text="Вход">
<font>
<Font size="14.0" />
</font>
</Button>
<Button fx:id="registrationButton" layoutX="400.0" layoutY="329.0" mnemonicParsing="false" text="Регистрация">
<font>
<Font size="14.0" />
</font>
</Button>
<Label fx:id="errorText" layoutX="524.0" layoutY="200.0" prefHeight="127.0" prefWidth="222.0" />
</children>
</AnchorPane>
chatWindow.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" style="-fx-background-color: #ffca77;" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.controllers.ChatController">
<children>
<TextArea fx:id="printMessageField" editable="false" layoutX="14.0" layoutY="43.0" prefHeight="461.0" prefWidth="538.0" />
<TextField fx:id="inputMessageField" layoutX="14.0" layoutY="528.0" prefHeight="26.0" prefWidth="429.0">
<font>
<Font size="14.0" />
</font>
</TextField>
<Button fx:id="sendMessageButton" layoutX="461.0" layoutY="527.0" mnemonicParsing="false" prefHeight="30.0" prefWidth="90.0" text="Отправить">
<font>
<Font size="14.0" />
</font>
</Button>
<TextArea fx:id="printUsersField" editable="false" layoutX="584.0" layoutY="149.0" prefHeight="281.0" prefWidth="200.0" />
<Label layoutX="617.0" layoutY="117.0" text="Пользователи в чате">
<font>
<Font size="14.0" />
</font>
</Label>
<Button fx:id="exitChatButton" layoutX="676.0" layoutY="556.0" mnemonicParsing="false" text="Выйти из чата">
<font>
<Font size="14.0" />
</font>
</Button>
<Button fx:id="sendFileButton" layoutX="444.0" layoutY="558.0" mnemonicParsing="false" text="Отправить файл">
<font>
<Font size="14.0" />
</font>
</Button>
<Button fx:id="menuFuncButton" layoutX="589.0" layoutY="43.0" mnemonicParsing="false" text="Дополнительные функции">
<font>
<Font size="14.0" />
</font>
</Button>
<Button fx:id="enterChatButton" layoutX="684.0" layoutY="519.0" mnemonicParsing="false" text="Войти в чат">
<font>
<Font size="14.0" />
</font>
</Button>
</children>
</AnchorPane>
Server.java
package server;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args){
try(ServerSocket server = new ServerSocket(1821)){
System.out.println("Server started");
while (true) {
try (
Socket socket = server.accept();
BufferedWriter writer =
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream()));
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
socket.getInputStream()))
) {
//Тут код вырезан
} catch (Exception ignored) {
}
}
} catch (IOException e){
throw new RuntimeException(e);
}
}
}