0

In Netbeans, I am trying to create a Desktop Application whose UI looks like below:

enter image description here

I am executing "adb logcat command" through Java code which loads 1000s of lines of logs in few seconds & I intend to display all of this information through jTable in NetBeans.

Using parameter: adb logcat -t 100 -> I am restricting the logs to 100 lines only right now. However the applet becomes unresponsive (or gets stuck in process() method) for 1000 lines or when removing such restriction on number of lines.

I am not sure whether I have properly implemented the SwingWorker thread in my code. I am looking for suggestions on how to improve the code for loading large amount of data dynamically without applet becoming unresponsive.

Following is the implemented code for the applet... with 2 functions:

  1. viewLogs() called from init() method of applet.
  2. SwingWorker implementation.

        public void viewLogs() throws IOException {
    
        String[] command = {"CMD","/C", "adb logcat -t 100"};
        ProcessBuilder probuilder = new ProcessBuilder( command );
        probuilder.directory(new File("c:\\Users\\k.garg\\Desktop\\"));
        Process process = probuilder.start();
    
        InputStream is = process.getInputStream();      
        InputStreamReader isr = new InputStreamReader(is);
        br = new BufferedReader(isr);
    
        DefaultTableModel model = (DefaultTableModel)jTable1.getModel();
        worker.execute();
    
        try {
            int exitVal = process.waitFor();
            System.out.println("exitVal = " + exitVal);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public class TableSwingWorker extends SwingWorker<DefaultTableModel, Object[]>{
    private DefaultTableModel tableModel;
    public TableSwingWorker(DefaultTableModel tableModel){
        this.tableModel = tableModel;
    }
    
    @Override
    protected DefaultTableModel doInBackground() throws Exception {
        Thread.sleep(2000); //added for initial UI to load
        System.out.println("Start populating");
        String line, date, time, loglevel, PID, TID, tag, message="";
        String log;
        int count = 0;
        while ((log = br.readLine()) != null) {
            count++;
    
            String[] splitLog = log.trim().split("\\s+");
            line = Integer.toString(count);
            date = splitLog[0];
            time = splitLog[1];
            PID = splitLog[2];
            TID = splitLog[3];
            loglevel = splitLog[4];
            tag = splitLog[5];
            for(int i=6; i<splitLog.length;i++){
                message += splitLog[i];
            }
            publish(new Object[]{line, date, time, PID, TID, loglevel, tag, message});
        }
        return tableModel;
    }
    
    @Override
    protected void process(List<Object[]> chunks) {
        System.out.println("Adding " + chunks.size() + " rows");
        for(Object[] row: chunks)
            tableModel.insertRow(0,row);
    }
    

    }

mKorbel
  • 109,525
  • 20
  • 134
  • 319
kkgarg
  • 1,246
  • 1
  • 12
  • 28
  • [An example of `SwingWorker` and `ProcessBuilder`](http://stackoverflow.com/questions/15801069/printing-a-java-inputstream-from-a-process/15801490#15801490) – MadProgrammer Mar 23 '17 at 07:41
  • [An example of `SwingWorker` and `JTable`](http://stackoverflow.com/questions/17414109/populate-jtable-with-large-number-of-rows/17415635#17415635) – MadProgrammer Mar 23 '17 at 07:41
  • [And another example of `SwingWorker` and `JTable`](http://stackoverflow.com/questions/26477744/how-to-search-subfolders-and-repaint-a-jtable-with-new-data-in-java/26478059#26478059) – MadProgrammer Mar 23 '17 at 07:42
  • As a general suggestion, the `Process` should be started in the `SwingWorker` because `waitFor` will block the Event Dispatching Thread and stop the UI from updating – MadProgrammer Mar 23 '17 at 07:44

1 Answers1

2

The key problem is...

try {
    int exitVal = process.waitFor();
    System.out.println("exitVal = " + exitVal);
} catch (InterruptedException e) {
    e.printStackTrace();
}

This is blocking the Event Dispatching Thread, preventing any possible updates from occurring until the Process completes, kind of defeating the purpose of using a SwingWorker.

You'd be better off executing the Process in the SwingWorker directly, something like...

public class TableSwingWorker extends SwingWorker<Integer, Object[]> {

    private DefaultTableModel tableModel;
    private int count;

    public TableSwingWorker(DefaultTableModel tableModel) {
        this.tableModel = tableModel;
    }

    @Override
    protected Integer doInBackground() throws Exception {
        count = 0;
        
        String[] command = {"CMD", "/C", "adb logcat -t 100"};
        ProcessBuilder probuilder = new ProcessBuilder(command);
        probuilder.directory(new File("c:\\Users\\k.garg\\Desktop\\"));
        Process process = probuilder.start();

        InputConsumer consumer = new InputConsumer(process.getInputStream());
        consumer.start();
        
        int result = process.waitFor();
        consumer.join();

        return result;
    }

    @Override
    protected void process(List<Object[]> chunks) {
        System.out.println("Adding " + chunks.size() + " rows");
        for (Object[] row : chunks) {
            tableModel.insertRow(0, row);
        }
    }
    
    protected void processOutput(String text) {
            count++;

            String[] splitLog = text.trim().split("\\s+");
            String line = Integer.toString(count);
            String date = splitLog[0];
            String time = splitLog[1];
            String PID = splitLog[2];
            String TID = splitLog[3];
            String loglevel = splitLog[4];
            String tag = splitLog[5];
            
            StringBuilder message = new StringBuilder(64);
            for (int i = 6; i < splitLog.length; i++) {
                message.append(splitLog[i]);
            }
            publish(new Object[]{line, date, time, PID, TID, loglevel, tag, message});
    }

    public class InputConsumer extends Thread {

        private InputStream is;

        public InputConsumer(InputStream is) {
            this.is = is;
            start();
        }

        @Override
        public void run() {
            try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
                String text = null;
                while ((text = br.readLine()) != null) {
                    processOutput(text);
                }
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }
    }
}

Okay, this might look a little "heavy" but it does two important things:

  1. It off loads the reading of the process's InputStream to another Thread, which allows us to...
  2. waitFor the process to exit so we get the exit value, which can be useful in diagnosing why somethings don't work from time to time

Other observations

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366