问题
My application is supposed to read and write to a Serial Port. The data is read in the EventListener of the PortReader class. I wish to assign this data to a global String variable (private String sPortReaderString) for further use. The global string variable should return its value by using a method called getPortReader() which simply returns the string sPortReaderString. In the application's JFrame I open the serial port connection, send a command for which I automatically receive a reply from serial device, and I display that reply output in a label. The problem is that the label is always blank since the sPortReaderString returned from getPortReader() has nothing assigned to it. I am very sure that the sPortReaderString gets assigned a value in the EvenListener. The problem seems to be that the method getPortReader() in the JFrame is called before any value had enough time to get assigned to sPortReaderString. Please take a look at my otuput and the code below:
The following is the output I get:
sPortReaderString: PortReader
Short example of portsMethod in JFrame:
public class MyJFrame extends javax.swing.JFrame {
public MySerialPort msp = new MySerialPort();
public MainJFrame() {
portsMethod();
}
private void portsMethod() {
msp.getPortNames();//Gets the name of the port (COM1 in my case)
msp.openPort();//Opens COM1 port
msp.getFirmwareVersion();//Prompts for device firmware version by sending a string command
msp.getPortReader();//Reads the reply from device
}
}
The following is the example of my Serial Port class:
public class MySerialPort {
private SerialPort serialPort;
private int iBaudRate = SerialPort.BAUDRATE_57600;
private int iDataBits = SerialPort.DATABITS_8;
private int iStopBits = SerialPort.STOPBITS_1;
private int iParity = SerialPort.PARITY_NONE;
private String sPortName;
private String sPortReaderString = "";
private StringBuilder sbPortReaderString = new StringBuilder();
public void getFirmwareVersion() {
sendPortCommand("<FirmVer>\r\n");
}
public void clearPortReader() {
sbPortReaderString.setLength(0);
}
public String getPortReader() {
System.out.print("sPortReaderString: " + sPortReaderString);
return sPortReaderString;
}
public void getPortNames() {
String[] sPorts = SerialPortList.getPortNames();
sPortName = sPorts[0];
}
public void openPort() {
serialPort = new SerialPort(sPortName);
try {
if (serialPort.openPort()) {
if (serialPort.setParams(iBaudRate, iDataBits, iStopBits, iParity)) {
serialPort.addEventListener(new PortReader(), SerialPort.MASK_RXCHAR
| SerialPort.MASK_RXFLAG
| SerialPort.MASK_CTS
| SerialPort.MASK_DSR
| SerialPort.MASK_RLSD);
} else {
serialPort.closePort();
}
} else {}
} catch (SerialPortException | HeadlessException ex) {}
}
private void sendPortCommand(String sSendPortCommand) {
if (sSendPortCommand.length() > 0) {
try {
serialPort.writeBytes(sSendPortCommand.getBytes());
} catch (Exception ex) {}
}
}
private class PortReader implements SerialPortEventListener {
private String sBuffer = "";
@Override
public void serialEvent(SerialPortEvent spe) {
if (spe.isRXCHAR() || spe.isRXFLAG()) {
if (spe.getEventValue() > 0) {
try {
//Read chars from buffer
byte[] bBuffer = serialPort.readBytes(spe.getEventValue());
sBuffer = new String(bBuffer);
SwingUtilities.invokeAndWait(
new Runnable() {
@Override
public void run() {
sbPortReaderString.append(sBuffer);
}
});
sPortReaderString = new String(sbPortReaderString);
//if I print sPortReaderString in here it is not blank and has the correct value
System.out.print("PortReader");
} catch (SerialPortException | InterruptedException | InvocationTargetException ex) {
}
}
}
}
}
}
回答1:
It seems pretty logical to me:
You have a first event handling method execution in the EDT which gets the firmware version and then gets the port reader. Getting the firmware version causes an event to be received, in a different thread (and thus in parallel to the execution of portsMethod()
in the EDT).
The event handling code invokes SwingUtilities.invokeAndWait()
. This call thus waits for the first event handling methd to complete, and then appends the received string to sbPortReaderString
. This append is thus done after the portsMethod
has completed.
The serial port offers an event-based mechanism. I would simply use it to propagate the event to one or several listeners in the EDT:
// accessed only from the EDT
private List<MyPortListener> portListeners = new ArrayList<MyPortListener>();
public void addMyPortListener(MyPortListener listener) {
portListeners.add(listener);
}
public void removeMyPortListener(MyPortListener listener) {
portListeners.remove(listener);
}
...
@Override
public void serialEvent(SerialPortEvent spe) {
...
final String receivedString = ...;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
for (MyPortListener listener : portListeners) {
listener.stringReveivedFromSerialPort(receivedString);
}
}
});
}
Side note: your code is hard to understand mainly because your variables and methods are badly named.
来源:https://stackoverflow.com/questions/9505892/java-string-return-method-is-called-before-any-value-is-assigned-to-string