2013-12-15 23 views
1

我需要能夠調用單獨的線程。線程分析一個文件並從中提取統計信息。如何從JavaFX中的單獨線程更新狀態和進度

文件分析最多可能需要2分鐘,在分析過程中,數據將打印到日誌中。

我想在前端有一個TextArea,需要打印出分析結果(因爲它分析),我也想有一個進度條來指示進度。所有這些都是在單獨的線程內部確定的。

我所做的是在UI類中創建一個方法,將字符串添加到文本區域並將此類的引用傳遞到啓動的線程。

我的主類

package trymutilthread; 

import javafx.application.Application; 
import javafx.application.Platform; 
import javafx.concurrent.Task; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.TextArea; 
import javafx.scene.layout.StackPane; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class TryMutilThread extends Application { 

    TextArea ta; 

    public static void main(String[] args) { 
     launch(args); 
    } 

    @Override 
    public void start(Stage primaryStage) { 
     Button btn = new Button(); 
     btn.setText("Start"); 
     btn.setOnAction(new EventHandler<ActionEvent>() { 
      @Override 
      public void handle(ActionEvent event) { 
       startScheduledExecutorService(); 
      } 
     }); 

     ta = new TextArea();   

     VBox vBox = new VBox(); 
     vBox.getChildren().addAll(btn, ta); 

     StackPane root = new StackPane(); 
     root.getChildren().add(vBox); 

     Scene scene = new Scene(root, 300, 750); 

     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    private void startScheduledExecutorService() { 
     final TryMutilThread classI = this; 

     Task<Void> task = new Task<Void>() { 
      @Override protected Void call() throws Exception { 
       try { 
        ta.appendText("Starting Thread\n"); 
        new SomeProcess(classI).doTheLogic(); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
       return null; 
      } 
     }; 

     Thread th = new Thread(task); 
     th.setDaemon(true); 
     th.start();  
    } 

    public void appendText(String string) {   
     ta.appendText(string); 
    } 
} 

是在線程

package trymutilthread; 

public class SomeProcess { 

    TryMutilThread taClass = null; 

    public SomeProcess (TryMutilThread taClass) { 
     this.taClass = taClass; 
    } 

    public void doTheLogic() throws Exception{   
     taClass.appendText("Staring Thread");   

     for (int i = 0; i < 5000; i++) { 
      taClass.appendText(String.valueOf(i)); 
     } 

     taClass.appendText("Ending Thread"); 
    } 
} 

當我執行這一點,仍然只輸出文本將TextArea一旦線程已經結束執行現在的類。

我在下面的2個職位一看:

JavaFX update textArea

Java client/server thread null pointer exception when quickly communicating messages

我不能讓打印到日誌中的數據,直到該進程已經結束。


我更新了我的代碼以創建一個任務。 但現在我收到以下錯誤,當它執行

Executing com.javafx.main.Main from F:\DEV\Projects\TryMutilThread\dist\run404234128\TryMutilThread.jar using platform C:\Program Files\Java\jdk1.7.0_10/bin/java 
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 trymutilthread.TryMutilThread.appendText(TryMutilThread.java:80) 
    at trymutilthread.SomeProcess.doTheLogic(SomeProcess.java:26) 
    at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:66) 
    at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:62) 
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1259) 
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:166) 
    at java.lang.Thread.run(Thread.java:722) 

然後我google'd錯誤,看來我需要把交互代碼在Platform.runlater()。

Java client/server thread null pointer exception when quickly communicating messages

我改變了類來執行線程要

package trymutilthread; 

import javafx.application.Platform; 

public class SomeProcess { 

    TryMutilThread taClass = null; 

    public SomeProcess(TryMutilThread taClass) { 
     this.taClass = taClass; 
    } 

    public void doTheLogic() throws Exception { 
     taClass.appendText("Staring Thread"); 

     for (int i = 0; i < 5000; i++) { 
      //remove this append line 
      //taClass.appendText(i + "\n");     
      //And replaced it with platform.runlater 
      Platform.runLater(new Runnable() { 
       @Override 
       public void run() { 
        taClass.appendText("AGREED" + "\n"); 
       } 
      }); 
     } 

     taClass.appendText("Ending Thread"); 
    } 
} 

它執行沒有任何錯誤,但現在它似乎是回到開始...的UI被凍結,直到所有被添加到TextArea中

+0

你沒有背景線程在你的示例代碼中;一切都在FX應用程序線程上運行。做你想做的最好的方法是使用javafx.concurrent.Task。請參閱有很多示例的[Javadocs](http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html)。 –

+0

James I更新了代碼以使用任務。它似乎工作,但只要我有足夠的代碼來添加它似乎它發生得很快,我得到一個空指針。我google'd錯誤,似乎我應該在平臺中添加代碼。runLater()但是再次凍結UI。我用詳細的評論修改了我的問題。 – Chrispie

回答

3

問題在於你的FX應用程序線程溢出請求過多;在您的Platform.runLater(...)調用之間沒有實際的工作發生。這個問題可能消失與實際應用,而不是這個測試,但以模仿實際的長期運行的工作,你可以把一個了Thread.sleep(...)在那裏:

import javafx.application.Application; 
import javafx.application.Platform; 
import javafx.concurrent.Task; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.ProgressBar; 
import javafx.scene.control.TextArea; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Stage; 

public class TextAreaBackgroundUpdateExample extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     final BorderPane root = new BorderPane(); 
     final TextArea textArea = new TextArea(); 
     final ProgressBar progress = new ProgressBar(); 
     final Button startButton = new Button("Start"); 

     final int maxCount = 5000 ; 

     startButton.setOnAction(new EventHandler<ActionEvent>() { 
      @Override 
      public void handle(ActionEvent event) { 
       Task<Void> task = new Task<Void>() { 
        @Override 
        protected Void call() throws Exception { 
         for (int i = 1; i <= maxCount; i++) { 
          Thread.sleep(10); 
          final int count = i ; 
          Platform.runLater(new Runnable() { 
           @Override 
           public void run() { 
            textArea.appendText("Processed part " + count + " (of "+maxCount+")\n");          
           } 
          }); 
          updateProgress(i, maxCount); 
         } 
         return null; 
        } 
       }; 
       progress.progressProperty().bind(task.progressProperty()); 
       Thread t = new Thread(task); 
       t.setDaemon(true); 
       t.start(); 
      } 
     }); 

     root.setCenter(textArea); 
     root.setTop(progress); 
     root.setBottom(startButton); 

     final Scene scene = new Scene(root); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 


    public static void main(String[] args) { 
     launch(args); 
    } 
} 
+0

感謝您的解釋和解決方案James :) – Chrispie