2015-07-14 21 views
6

tôi có một Ứng dụng JavaFX và trong đó có một nhiệm vụ đồng thời. Trong khi nhiệm vụ đang chạy, tôi muốn thêm thông điệp từ updateMessage() để một TextAreaJavaFX ChangeListener không phải lúc nào cũng hoạt động

vì các ràng buộc không nối thêm văn bản mới cho TextArea, tôi sử dụng một ChangeListener

worker.messageProperty().addListener((observable, oldValue, newValue) -> { 
    ta_Statusbereich.appendText("\n" + newValue); 
}); 

Đó là làm việc nhưng không phải trên mọi thay đổi. Tôi đã kiểm tra nó với một System.out.println() và tính vào nhiệm vụ từ 1 để 300

for (Integer i = 1; i <= 300; i++) { 
    updateMessage(i.toString()); 
    System.out.println(i.toString()); 
} 

println() này trong Task mang lại cho tôi những gì tôi muốn 1,2,3,4,5 , 6,7,8, v.v. nhưng TextArea của tôi cho thấy 1,4,5,8,9 i sau đó thêm một println trong ChangeListener và nhận được kết quả tương tự, 1,4,5,8,9 (kết quả là ngẫu nhiên không phải lúc nào cũng là 1,4,5 ...)

tại sao? có cách nào khác để thêm văn bản tin nhắn vào TextAres, có thể với ràng buộc không?

+0

"Tại sao?": Giải thích đầy đủ trong [tài liệu] (http://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html#updateMessage -java.lang.String-). Bạn thực sự muốn làm gì ở đây? (Tôi giả sử nó không chỉ để hiển thị các giá trị 1-300 trong một vùng văn bản, vì bạn sẽ không cần một nhiệm vụ cho điều đó.) –

+0

tác vụ đổi tên tệp trong một thư mục đã chọn qua mạng và tôi cố liệt kê tất cả các tệp được xử lý trong textarea. giống như thiết lập in các tệp được sao chép. – Garog

Trả lời

14

Thuộc tính message được thiết kế dưới dạng thuộc tính chứa "thông báo hiện tại" cho task: nghĩa là trường hợp sử dụng mục tiêu giống như thông báo trạng thái. Trong trường hợp sử dụng này, nó không quan trọng nếu một thư được lưu trữ trong thuộc tính chỉ trong một thời gian rất ngắn không bao giờ bị chặn. Thật vậy, documentation for updateMessage() trạng thái:

Các cuộc gọi đến updateMessage được coalesced và chạy sau đó trên các chủ đề ứng dụng FX , vì vậy các cuộc gọi đến updateMessage, thậm chí từ các chủ đề ứng dụng FX , có thể không nhất thiết dẫn đến cập nhật ngay lập tức để này tài sản, và giá trị tin nhắn trung gian có thể được kết hợp với lưu trên thông báo sự kiện.

(nhấn mạnh của tôi). Vì vậy, trong ngắn hạn, một số giá trị được chuyển đến updateMessage(...) có thể không bao giờ thực sự được đặt làm giá trị của messageProperty nếu chúng được superceded nhanh chóng bởi một giá trị khác. Nói chung, bạn chỉ có thể mong đợi một giá trị được quan sát mỗi lần khung được hiển thị trên màn hình (60 lần mỗi giây hoặc ít hơn). Nếu bạn có một trường hợp sử dụng, điều quan trọng là bạn muốn quan sát mọi giá trị, thì bạn cần phải sử dụng một cơ chế khác.

Việc triển khai rất ngây thơ sẽ chỉ sử dụng Platform.runLater(...) và cập nhật trực tiếp vùng văn bản. Tôi không khuyên bạn nên thực hiện điều này, vì bạn có nguy cơ tràn ngập Chủ đề ứng dụng FX với quá nhiều cuộc gọi (lý do chính xác tại sao updateMessage(...) kết hợp các cuộc gọi), khiến giao diện người dùng không phản hồi. Tuy nhiên, quá trình triển khai này sẽ như sau:

for (int i = 1 ; i <= 300; i++) { 
    String value = "\n" + i ; 
    Platform.runLater(() -> ta_Statusbereich.appendText(value)); 
} 

Một tùy chọn khác là thực hiện từng thao tác riêng biệt và thực hiện chúng song song trong một số người thi hành. Nối thêm vào vùng văn bản trong bộ xử lý của mỗi nhiệm vụ là onSucceeded. Thực hiện điều này, thứ tự của các kết quả không được định trước, vì vậy nếu trật tự là rất quan trọng, đây không phải là một cơ chế thích hợp:

final int numThreads = 8 ; 
Executor exec = Executors.newFixedThreadPool(numThreads, runnable -> { 
    Thread t = Executors.defaultThreadFactory().newThread(runnable); 
    t.setDaemon(true); 
    return t ; 
}); 

// ... 

for (int i = 1; i <= 300; i++) { 
    int value = i ; 
    Task<String> task = new Task<String>() { 
     @Override 
     public String call() { 
      // in real life, do real work here... 
      return "\n" + value ; // value to be processed in onSucceeded 
     } 
    }; 
    task.setOnSucceeded(e -> ta_Statusbereich.appendText(task.getValue())); 
    exec.execute(task); 
} 

Nếu bạn muốn làm tất cả điều này từ một nhiệm vụ duy nhất, và kiểm soát trật tự, sau đó bạn có thể đặt tất cả thư vào một số BlockingQueue, nhận thư từ hàng đợi chặn và đặt chúng vào vùng văn bản trên chuỗi Ứng dụng FX.Để đảm bảo bạn không tràn ngập chuỗi ứng dụng FX với quá nhiều cuộc gọi, bạn nên tiêu thụ các tin nhắn từ hàng đợi không quá một lần cho mỗi lần hiển thị khung hình cho màn hình. Bạn có thể sử dụng một AnimationTimer cho mục đích này: đó là phương pháp handle được đảm bảo sẽ được gọi một lần cho mỗi lần hiển thị khung. Này trông giống như:

BlockingQueue<String> messageQueue = new LinkedBlockingQueue<>(); 

Task<Void> task = new Task<Void>() { 
    @Override 
    public Void call() throws Exception { 
     final int numMessages = 300 ; 
     Platform.runLater(() -> new MessageConsumer(messageQueue, ta_Statusbereich, numMessages).start()); 
     for (int i = 1; i <= numMessages; i++) { 
      // do real work... 
      messageQueue.put(Integer.toString(i)); 
     } 
     return null ; 
    } 
}; 
new Thread(task).start(); // or submit to an executor... 

// ... 

public class MessageConsumer extends AnimationTimer { 
    private final BlockingQueue<String> messageQueue ; 
    private final TextArea textArea ; 
    private final numMessages ; 
    private int messagesReceived = 0 ; 
    public MessageConsumer(BlockingQueue<String> messageQueue, TextArea textArea, int numMessages) { 
     this.messageQueue = messageQueue ; 
     this.textArea = textArea ; 
     this.numMessages = numMessages ; 
    } 
    @Override 
    public void handle(long now) { 
     List<String> messages = new ArrayList<>(); 
     messagesReceived += messageQueue.drainTo(messages); 
     messages.forEach(msg -> textArea.appendText("\n"+msg)); 
     if (messagesReceived >= numMessages) { 
      stop(); 
     } 
    } 
} 
+2

câu trả lời như thế này là lý do tại sao tôi yêu cầu trên nền tảng này! wow và tuyệt vời, cảm ơn bạn rất nhiều. Đối với những người như tôi chỉ có một số sciolism. nắm giữ rất nhiều thông tin và kiến ​​thức ... khi bạn phải khai thác nội dung đó ra khỏi mô tả api ... người đàn ông địa ngục ... tôi đã cố gắng chạy ứng dụng nhưng đã chính xác những gì bạn dự đoán khi đổi tên tệp trên máy cục bộ và không kết thúc mạng. thứ tự cũng quan trọng, bởi vì tôi đếm các tập tin và in số vào màn hình. vì vậy cuối cùng tôi phải chọn số 3. :) – Garog

+0

Đây là một câu trả lời tuyệt vời! Giúp tôi với tình hình tôi có chính xác! tùy chọn 3 là con đường để đi! – WillZ

+1

Vâng, đây là một câu trả lời tuyệt vời. Lựa chọn 3 là cách tốt nhất để đi cho tôi. Tuy nhiên, bằng cách sử dụng 'messages.forEach (msg -> textArea.appendText (" \ "+ msg));' là một chút chậm đối với tôi. Thay vào đó, tôi đã nối tất cả các dòng trước khi thêm chúng vào vùng văn bản, ví dụ: 'textArea.appendText (String.join (" ", messages))'. Tôi không cần phải thêm ký tự dòng mới vì thư của tôi đã có dòng mới trong đó. – Thylossus

Các vấn đề liên quan