Platform.runLater và Task trong JavaFX


88

Tôi đã thực hiện một số nghiên cứu về điều này nhưng tôi vẫn RẤT bối rối để nói rằng ít nhất.

Có ai có thể cho tôi một ví dụ cụ thể về thời điểm sử dụng Taskvà thời điểm sử dụng Platform.runLater(Runnable);? Sự khác biệt chính xác là gì? Có một quy tắc vàng khi sử dụng bất kỳ cái nào trong số này không?

Cũng sửa cho tôi nếu tôi sai nhưng hai "Đối tượng" này không phải là cách tạo một luồng khác bên trong luồng chính trong GUI (được sử dụng để cập nhật GUI)?

Câu trả lời:


108

Sử dụng Platform.runLater(...)cho các hoạt động nhanh chóng và đơn giản và Taskcho các hoạt động phức tạp và lớn.

Ví dụ: Tại sao chúng ta không thể sử dụng Platform.runLater(...)cho các phép tính dài (Lấy từ tài liệu tham khảo bên dưới).

Vấn đề: Chuỗi nền chỉ đếm từ 0 đến 1 triệu và cập nhật thanh tiến trình trong giao diện người dùng.

Mã sử ​​dụng Platform.runLater(...):

final ProgressBar bar = new ProgressBar();
new Thread(new Runnable() {
    @Override public void run() {
    for (int i = 1; i <= 1000000; i++) {
        final int counter = i;
        Platform.runLater(new Runnable() {
            @Override public void run() {
                bar.setProgress(counter / 1000000.0);
            }
        });
    }
}).start();

Đây là một đoạn mã ghê tởm, một tội ác chống lại thiên nhiên (và lập trình nói chung). Đầu tiên, bạn sẽ mất các tế bào não khi chỉ nhìn vào tổ hợp các Runnables kép này. Thứ hai, nó sẽ làm tràn ngập hàng đợi sự kiện với ít Runnables - thực tế là một triệu trong số chúng. Rõ ràng, chúng tôi cần một số API để giúp dễ dàng hơn khi viết các công cụ nền sau đó giao tiếp lại với giao diện người dùng.

Mã sử ​​dụng Tác vụ:

Task task = new Task<Void>() {
    @Override public Void call() {
        static final int max = 1000000;
        for (int i = 1; i <= max; i++) {
            updateProgress(i, max);
        }
        return null;
    }
};

ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();

nó không mắc phải lỗi nào trong mã trước

Tham khảo: Worker Threading trong JavaFX 2.0


Ví dụ về Tác vụ trong liên kết Ứng dụng Ensemble sẽ không hoạt động nữa.
Cugomastik

Đây là liên kết chính xác nhưng tôi không thể chỉnh sửa nó trong vì bản chỉnh sửa chỉ có 2 ký tự.
Aerus

29
Để nói rằng một cho các hoạt động "nhanh" và một cho các hoạt động "phức tạp" là một chút sai lầm. Sự khác biệt chính, sau tất cả, là một cho phép chạy mã trong chuỗi GUI JFX từ một nơi khác, và một trong những điều đó hoàn toàn ngược lại - cho phép chạy mã trong chuỗi nền từ chuỗi GUI (thêm một phần thưởng là có thể giao tiếp với luồng GUI trong quy trình).
Sergei Tachenov

Tôi muốn lưu Hình ảnh của từng cảnh - và việc này sẽ mất thời gian. Điều đó có gây ra sự cố khi chạy trên một chuỗi riêng biệt thông qua Tác vụ không? Tôi hiểu rằng tất cả các công việc liên quan đến gui cần phải diễn ra trên chuỗi FxApplication.
StephenBoesch

@ Sergey-Tachenov Vì vậy, bạn sử dụng runLater () từ một chuỗi Tác vụ để cập nhật chuỗi GUI trong trường hợp bạn muốn làm nhiều việc hơn là chỉ cập nhật một thuộc tính đơn lẻ như tiến trình?
simpleuser

58
  • Platform.runLater: Nếu bạn cần cập nhật một thành phần GUI từ một luồng không phải GUI, bạn có thể sử dụng nó để đặt bản cập nhật của mình vào hàng đợi và nó sẽ được luồng GUI xử lý sớm nhất có thể.
  • Tasktriển khai Workergiao diện được sử dụng khi bạn cần chạy một tác vụ dài bên ngoài luồng GUI (để tránh đóng băng ứng dụng của bạn) nhưng vẫn cần tương tác với GUI ở một số giai đoạn.

Nếu bạn đã quen thuộc với Swing, cái trước tương đương với SwingUtilities.invokeLatervà cái sau tương đương với khái niệm SwingWorker.

Các javadoc của công tác cung cấp cho nhiều ví dụ mà nên làm rõ làm thế nào họ có thể được sử dụng. Bạn cũng có thể tham khảo hướng dẫn về đồng thời .


Xin cảm ơn Bạn có thể cho một ví dụ nhỏ về cách sử dụng nền tảng không? Bạn có thể sử dụng nó ngoài luồng gui hay không? Và các ví dụ nhiệm vụ trong các tài liệu thực sự không rõ ràng
Marc Rasmussen

Có Platform.runLater có thể được sử dụng bên ngoài luồng GUI - đó là mục đích chính của nó. Bạn có thể thấy hướng dẫn này về các tác vụ nhiều thông tin hơn so với javadoc.
assylias

3
Bạn sử dụng Platform.runLater như thế này:Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias

làm thế nào tôi sẽ có thể sử dụng các thành phần GUI rồi =: S
Marc Rasmussen

1
@KevinMeredith Không nên gọi phương thức gọi của Tác vụ trên luồng FX - nhưng Tác vụ cung cấp các phương thức cầu nối (updateProgress, v.v.) chạy trên luồng FX. Xem javadoc & hướng dẫn để biết thêm thông tin.
assylias

12

Bây giờ nó có thể được thay đổi thành phiên bản lambda

@Override
public void actionPerformed(ActionEvent e) {
    Platform.runLater(() -> {
        try {
            //an event with a button maybe
            System.out.println("button is clicked");
        } catch (IOException | COSVisitorException ex) {
            Exceptions.printStackTrace(ex);
        }
    });
}

8
Nếu bạn đang xử lý một sự kiện GUI, bạn đang ở trong chuỗi GUI. Tại sao bạn sử dụng runLater () sau đó?
TFuto

Trong khi bạn nói đúng, câu trả lời của Caglar đang cố gắng làm nổi bật việc sử dụng biểu thức lambda, không nhất thiết phải đưa ra một ví dụ mã hóa tốt. Tôi sử dụngPlatform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin

2

Một lý do để sử dụng Platform.runLater () rõ ràng có thể là bạn ràng buộc một thuộc tính trong ui với một thuộc tính dịch vụ (kết quả). Vì vậy, nếu bạn cập nhật thuộc tính dịch vụ ràng buộc, bạn phải thực hiện việc này thông qua runLater ():

Trong chuỗi giao diện người dùng còn được gọi là chuỗi ứng dụng JavaFX:

...    
listView.itemsProperty().bind(myListService.resultProperty());
...

trong Triển khai dịch vụ (nhân viên nền):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.