问题
I'm using JavaFX to create a simple messenger client. The classes in question are:
MainMenuController.class: this class handles JavaFX ui logic such as printing chat messages to text area
Server.class: controls server socket and in/out logic
Client.class: controls client socket and in/out logic
I'm able to run the application perfectly on local host, except my program throws a nullpointerexception when I try to send messages too quickly. I'll past the relevant code, and then I'll explain what I've tried to solve this bug.
MainMenuController.java (exception traced to this function)
public static void printMessage(String sender, String msg) {
//msgHistory is a textArea
msgHistory.appendText(sender + ": " +msg + "\n");
}
Server.java
try {
servSocket = new ServerSocket(serverPort);
MainMenuController.printMessage("Server running on port: "
+ serverPort + ". . .");
// block till we accept connection request
clientSocket = servSocket.accept();
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
// receive and set friend's alias
friendName = in.readLine();
aliasSet = true;
String input;
// while connection is present, poll server socket input for new
// messages
while (true) {
Thread.sleep(100);
input = in.readLine();
if (input == null) {
break;
}
//update status
MainMenuController.printMessage(friendName, input); //Line traced to exception
}
}
Client.java
try {
socket = new Socket (serverAddr, portNum);
MainMenuController.printMessage("Connected!");
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//send server our alias
out.println(myName);
} catch (IOException e) {
System.err.println("Accept failed.");
}
I believe the exception is caused by the client and server trying to write to the textArea in MainMenuController at the same time. I've experimented with adding a thread.sleep(x) before the server prints to textArea. I've also implemented a public lock in the MainMenuController class. In each call to printMessage in both the server and client, I wait until the lock is free before calling the printMessage method (I set the lock before calling printMessage, then free it once the method has returned). I took this code out to make it more readable. Neither of these solutions have worked and I'm not sure where to look.
the stack trace:
Exception in thread "Thread-4" java.lang.NullPointerException
at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.computeLinePadding(NGTextHelper.java:405)
at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.access$200(NGTextHelper.java:292)
at com.sun.javafx.sg.prism.NGTextHelper.buildTextLines(NGTextHelper.java:2357)
at com.sun.javafx.sg.prism.NGTextHelper.validateText(NGTextHelper.java:1847)
at com.sun.javafx.sg.prism.NGTextHelper.getCaretShape(NGTextHelper.java:1435)
at javafx.scene.text.Text.getDecorationShapes(Text.java:1150)
at javafx.scene.text.Text.impl_geomChanged(Text.java:757)
at javafx.scene.text.Text$1.invalidated(Text.java:214)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:127)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at com.sun.javafx.scene.control.skin.TextAreaSkin$17.invalidated(TextAreaSkin.java:610)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:359)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1034)
at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1038)
at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:978)
at javafx.scene.control.TextInputControl$TextProperty.access$200(TextInputControl.java:950)
at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:119)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.scene.control.TextArea$TextAreaContent.insert(TextArea.java:196)
at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:373)
at javafx.scene.control.TextInputControl.insertText(TextInputControl.java:308)
at javafx.scene.control.TextInputControl.appendText(TextInputControl.java:298)
at application.MainMenuController.printMessage(MainMenuController.java:96)
at application.Server.run(Server.java:79)
at java.lang.Thread.run(Unknown Source)
Edit: This error only occurs with textArea.appendText(string). No exception is thrown with textArea.setText(string).
回答1:
You are not supposed to call JavaFX methods from an arbitrary thread, but only from the JavaFX Application Thread. Wrap your msgHistory.appendText
in a Platform.runLater() call.
回答2:
You aren't checking friendName
for null. Any readLine()
invocation can return null.
NB Sleeping before calling readLine()
is literally a waste of time. It will block until data is available.
来源:https://stackoverflow.com/questions/19192574/java-client-server-thread-null-pointer-exception-when-quickly-communicating-me