2015-12-11 3 views
0

Im, создающий приложение, которое (подведя итог в самых простых выражениях) действует как интерфейс к другому приложению, поэтому вместо того, чтобы просматривать вещи через консоль, я могу увидеть их в хорошем окне Swing. Я сделал более раннюю версию этого, которая просто выводит все в JTextArea как обычный текст. Он работал нормально, и у меня не было проблем с этим. Но, чувствуя потребность в расширении этой самой базовой программы немного, я решил попробовать и раскрасить сообщения, чтобы кто-то, кроме меня, мог найти путь вокруг вывода немного легче. Первая часть программы включает в себя следующее:JEditorPane мерцает при обновлении и выдает необъяснимо большие объемы использования памяти

class ServerIOStream extends Thread { 

    private boolean _isRunning = false; 
    private Process _serverProcess = null; 
    private BufferedReader input = null; 
    private PrintWriter writer = null; 

    public void run() { 
     try { 
      _serverProcess = Runtime.getRuntime().exec(PropertiesReader.getLaunchString()); 
      input = new BufferedReader(new InputStreamReader(_serverProcess.getInputStream())); 
      _isRunning = true; 
      String line; 
      while ((line = input.readLine()) != null) 
       MainWindow.writeToOutput(line, 0); 
      _isRunning = false; 
      MainWindow.writeToOutput("Service has stopped...", 1); 
     } catch (IOException ex) { 
      MainWindow.writeToOutput(ex.getMessage(), 3); 
      _isRunning = false; 
     } 
    } 

    public boolean ServerStatus(){ 
     return _isRunning; 
    } 
} 

Мои извинения, если вещи, кажется, немного грязный и некоторые вещи не имеет смысла, так как это довольно поздно, и я был возиться с кодом странными способами, чтобы попытаться чтобы это работало по желанию.

СЕЙЧАС, как вы можете видеть, при запуске нить забирает время выполнения, а затем через цикл просто выплевывает то, что получает в метод. Этот метод (код, показанный ниже) отвечает за доставку контента в JEditorPane. Вот как работает функция MainWindow.writeToOutput().

private static String output; 
public static void writeToOutput(String message, int inputType){ 
    String finalMessage = null; 
    String type = null; 
    switch(inputType){ 
     case 0: // For input from the server 
      finalMessage = message; 
      break; 
     case 1: // General info from the GUI 
      type = "INFO"; 
      break; 
     case 2: // Warnings from the GUI 
      type = "WARN"; 
      break; 
     case 3: // Error messages from the GUI 
      type = "ERROR"; 
      break; 
     default: // Unknown messages 
      type = "UNKNOWN"; 
      break; 
    } 
    if(inputType == 1 || inputType == 2 || inputType == 3){ 
     finalMessage = String.format("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + " GUI %s" + "]: %s", type, message); 
     if(inputType == 1) 
      finalMessage = "<font color=\"" + PropertiesReader.getInfoColor() + "\">" + finalMessage + "</font>"; 
     else if(inputType == 2) 
      finalMessage = "<font color=\"" + PropertiesReader.getWarnColor() + "\">" + finalMessage + "</font>"; 
     else if(inputType == 3) 
      finalMessage = "<font color=\"" + PropertiesReader.getErrColor() + "\">" + finalMessage + "</font>"; 
    } 
    else if(inputType == 0){ 
     try{ 
      String s = finalMessage; 
      s = s.substring(s.indexOf("[") + 1); 
      s = s.substring(0, s.indexOf("]")); 

      if(s.contains("INFO")) 
       finalMessage = "<font color=\"" + PropertiesReader.getInfoColor() + "\">" + finalMessage + "</font>"; 
      else if(s.contains("WARN")) 
       finalMessage = "<font color=\"" + PropertiesReader.getWarnColor() + "\">" + finalMessage + "</font>"; 
      else if(s.contains("ERROR")) 
       finalMessage = "<font color=\"" + PropertiesReader.getErrColor() + "\">" + finalMessage + "</font>"; 
     } 
     catch(StringIndexOutOfBoundsException ex){ 
      finalMessage = "<font color=\"red\">" + finalMessage + "</font>"; 
     } 
    } 

    if(output != null) 
     output += finalMessage + "<br>"; 
    else 
     output = finalMessage + "<br>"; 

    ServerOutputTextArea.setText(output); 

    if(inputType == 0 || inputType == 1 || inputType == 2) 
     System.out.println(finalMessage); 
    else 
     System.err.println(finalMessage); 
    if(AutoScrollCheckbox.isSelected()) 
     ServerOutputTextArea.setCaretPosition(ServerOutputTextArea.getDocument().getLength()); 

    finalMessage = null; 
    message = null; 
    System.out.println("\nLength: " + output.length()); 
} 

Приносим извинения, если код трудно понять или бессмысленно. Как я уже говорил, поздно, и я просто возился с ним разными способами, пытаясь заставить его работать.

В принципе, вход идет следующим образом. Аргумент 1 - это сообщение, а аргумент 2 - тип сообщения, представленного как int. 0 используется для представления сообщений, поступающих из среды выполнения, отображаемой ранее, 1 для общей информации, 2 для предупреждений, а 3 - для ошибок/исключений. Если аргумент 2 равен нулю, тогда первый фрагмент сообщения (обычно отформатированный как таковой: «[HH: MM: SS Type]») будет расчленен для определения его типа.

Код, который я сделал, очень временный и неуклюжий, и я это понимаю, однако это не тема обсуждения прямо сейчас. Проблема в том, что во-первых, каждый раз, когда на JEditorPane выводится что-то новое, панель кратковременно пустая (на долю секунды), прежде чем текст снова появится с новым сообщением внизу. Честно говоря, когда это происходит из одного сообщения, его трудно заметить, но когда процесс выплескивает сообщения влево и вправо, программа будет мерцать и слишком много вспыхивать, а также вырезать и прокручивать вниз до случайных точек в приложении, необъяснимо, поскольку автопрокрутка борется с поддерживать.

Вторая проблема - проблема с памятью. Что происходит, когда приложение запускается первым, и до того, как процесс действительно начал развиваться, он спокойно сидит вокруг 50 МБ использования памяти и не доводит до минимума ни один из моих драгоценных циклов процессора. Но как только процесс дает сообщения быстрее и быстрее, использование памяти, по-видимому, возрастает экспоненциально. Переход от 50 МБ, до 100 МБ, до 500 МБ, и обычно до максимальной загрузки от 1,5 до 1,8 ГБ.

Сначала я не был уверен, что это вызвано JEditorPane, но затем я провел небольшой эксперимент. Я вернулся к использованию JTextArea и оставил почти все одинаково и уверенно, даже когда процесс выплескивал информацию, подобную сумасшедшей, я только видел пики около 70 МБ использования памяти, а мерцающая и автопрокрутка, которую я описал, существует.

Вот что я видел в тестировании:

  • JTextArea | Выходные данные содержали 47553 символа, а использование памяти было 52 МБ
  • JEditorPane | Выход содержал 50531 символов, и использование памяти был 1550MB

ТАКЖЕ: На одной заключительной ноте я хотел бы сказать, что я до сих пор не очень опытный с Java, или когда дело доходит до создания приложений с помощью визуальных интерфейсов Генеральная. Большое спасибо всем, кто отвечает.

+1

Трудно быть уверенным, но вы, кажется, нарушаете правила нитей Swing, которые не помогут. См. [Параллелизм в Swing] (http://docs.oracle.com/javase/tutorial/uiswing/concurrency/). Текстовые компоненты имеют тенденцию требовать большой объем памяти для того, что считается небольшим количеством текста. Возможно, лучше было бы взаимодействовать с «Документом» текстовых компонентов и вставлять текст, а затем использовать «setText» – MadProgrammer

+0

Некоторые альтернативы [здесь] (http://stackoverflow.com/q/25526833/230513); вы можете 'exec()' из 'doInBackground()'. – trashgod

ответ

0

Я решил попробовать и цвет пальто сообщения

Я хотел бы использовать JTextPane вместо JEditor панели. Я считаю, что работать с атрибутами проще, чем работать с HTML, и не знаю точно, но угадайте, что потребность в памяти будет меньше, поскольку вам не нужно анализировать HTML.

Использование атрибутов прямо вперед:

JTextPane textPane = new JTextPane(); 
textPane.setText("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight"); 
StyledDocument doc = textPane.getStyledDocument(); 

// Define a keyword attribute 

SimpleAttributeSet keyWord = new SimpleAttributeSet(); 
StyleConstants.setForeground(keyWord, Color.RED); 
StyleConstants.setBackground(keyWord, Color.YELLOW); 
StyleConstants.setUnderline(keyWord, Boolean.TRUE); 
StyleConstants.setBold(keyWord, true); 

// Define green attribute 

SimpleAttributeSet green = new SimpleAttributeSet(); 
StyleConstants.setFontFamily(green, "Courier New Italic"); 
StyleConstants.setForeground(green, Color.GREEN); 


// Change attributes on some text 

doc.setCharacterAttributes(0, 5, keyWord, false); 
doc.setCharacterAttributes(20, 4, green, true); 

// Add some text with attributes 

try 
{ 
    doc.insertString(3, "\ngreen text", green); 
} 
catch(Exception e) {} 
0

Я хотел бы использовать StringBuilder вместо String, для

private static String output 

Также простой setText() заменен setDocument()

Document doc=ServerOutputTextArea.getEditorKit().createDefaultDocument(); 
ServerOutputTextArea.getEditorKit().read(new StringReader(output),doc,0); 
ServerOutputTextArea.setDocument(doc); 

remove(0, getLength())setText() вызовов для документа и может оставить некоторые кэшированные данные - позиции, кеш изображений и т. д.

Смежные вопросы