Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
176 views
in Technique[技术] by (71.8m points)

multithreading - java - string return method is called before any value is assigned to string

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>
");
    }

    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) {
                    }
                }
            }
        }
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...