Platform.runLater และ Task ใน JavaFX


90

ฉันได้ทำการค้นคว้าเกี่ยวกับเรื่องนี้มาบ้าง แต่ฉันก็ยังสับสนมากที่จะพูดอย่างน้อยที่สุด

ใครช่วยยกตัวอย่างที่เป็นรูปธรรมว่าควรใช้Taskเมื่อไรและควรใช้เมื่อPlatform.runLater(Runnable);ใด อะไรคือความแตกต่าง? มีกฎทองในการใช้สิ่งเหล่านี้หรือไม่?

แก้ไขฉันด้วยถ้าฉันผิด แต่ "Object" ทั้งสองนี้ไม่ใช่วิธีการสร้างเธรดอื่นภายในเธรดหลักใน GUI (ใช้สำหรับอัปเดต GUI)?

คำตอบ:


110

ใช้Platform.runLater(...)สำหรับการดำเนินการที่ง่ายและรวดเร็วและสำหรับการดำเนินการTaskที่ซับซ้อนและใหญ่

ตัวอย่าง: เหตุใดเราจึงไม่สามารถใช้Platform.runLater(...)สำหรับการคำนวณแบบยาวได้ (นำมาจากข้อมูลอ้างอิงด้านล่าง)

ปัญหา: เธรดพื้นหลังซึ่งนับตั้งแต่ 0 ถึง 1 ล้านและอัปเดตแถบความคืบหน้าใน UI

รหัสโดยใช้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();

นี่เป็นรหัสที่น่ากลัวอาชญากรรมต่อธรรมชาติ (และการเขียนโปรแกรมโดยทั่วไป) ขั้นแรกคุณจะสูญเสียเซลล์สมองเพียงแค่มองไปที่การซ้อนกันของ Runnables ประการที่สองมันจะล้นคิวงานด้วย Runnables เล็ก ๆ น้อย ๆ ซึ่งในความเป็นจริงแล้วนับล้าน เห็นได้ชัดว่าเราต้องการ API บางตัวเพื่อให้ง่ายต่อการเขียนโปรแกรมทำงานเบื้องหลังซึ่งจะสื่อสารกลับด้วย UI

รหัสโดยใช้งาน:

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();

มันได้รับความทุกข์ทรมานจากข้อบกพร่องใด ๆ ที่แสดงในรหัสก่อนหน้านี้

ข้อมูลอ้างอิง: Worker Threading ใน JavaFX 2.0


ตัวอย่างงานในลิงค์ Ensemble App จะไม่ทำงานอีกต่อไป
Cugomastik

นี่เป็นลิงก์ที่ถูกต้องแต่ฉันไม่สามารถแก้ไขได้เนื่องจากการแก้ไขมีเพียง 2 อักขระ
Aerus

29
การพูดว่าอย่างหนึ่งสำหรับการดำเนินการแบบ "ด่วน" และอีกแบบสำหรับการดำเนินการที่ "ซับซ้อน" นั้นทำให้เข้าใจผิดเล็กน้อย ความแตกต่างที่สำคัญคือสิ่งที่อนุญาตให้เรียกใช้โค้ดในเธรด JFX GUI จากที่อื่นและอีกอันหนึ่งทำตรงกันข้าม - อนุญาตให้เรียกใช้โค้ดในเธรดพื้นหลังจากเธรด GUI (เพิ่มโบนัสของการเป็น สามารถสื่อสารกับเธรด GUI ได้ในกระบวนการ)
Sergei Tachenov

ฉันต้องการบันทึกภาพของแต่ละฉาก - ซึ่งจะต้องใช้เวลา จะทำให้เกิดปัญหาเมื่อเรียกใช้เธรดแยกจาก Task หรือไม่ ฉันเข้าใจว่างานทั้งหมดที่เกี่ยวข้องกับ gui ต้องเกิดขึ้นบนเธรด FxApplication
StephenBoesch

@ Sergey-Tachenov ดังนั้นคุณจึงใช้ runLater () จากเธรดงานเพื่ออัปเดตเธรด GUI ในกรณีที่คุณต้องการทำมากกว่าเพียงแค่อัปเดตคุณสมบัติเดียวเช่นความคืบหน้า?
simpleuser

59
  • Platform.runLater: หากคุณต้องการอัปเดตคอมโพเนนต์ GUI จากเธรดที่ไม่ใช่ GUI คุณสามารถใช้สิ่งนั้นเพื่อวางการอัปเดตของคุณในคิวและเธรด GUI จะจัดการโดยเร็วที่สุด
  • Taskใช้Workerอินเทอร์เฟซที่ใช้เมื่อคุณต้องการรันงานที่ยาวนานนอกเธรด GUI (เพื่อหลีกเลี่ยงการแช่แข็งแอปพลิเคชันของคุณ) แต่ยังคงต้องโต้ตอบกับ GUI ในบางขั้นตอน

หากคุณคุ้นเคยกับ Swing อดีตจะเทียบเท่ากับSwingUtilities.invokeLaterแนวคิดSwingWorkerหลัง

Javadoc ของงานให้ตัวอย่างมากมายที่ควรชี้แจงว่าพวกเขาสามารถนำมาใช้ นอกจากนี้คุณยังสามารถดูการสอนเกี่ยวกับพ้อง


ขอบคุณช่วยยกตัวอย่างเล็ก ๆ น้อย ๆ เกี่ยวกับการใช้งานแพลตฟอร์มได้หรือไม่? ใช้นอกกระทู้กุ่ยได้หรือ? และตัวอย่างงานในเอกสารนั้นไม่ชัดเจนจริงๆ
Marc Rasmussen

ใช่ Platform.runLater สามารถใช้นอกเธรด GUI - นั่นคือจุดประสงค์หลัก คุณอาจพบบทช่วยสอนนี้เกี่ยวกับงานที่ให้ข้อมูลมากกว่า javadoc
assylias

3
คุณใช้ Platform.runLater ดังนี้Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias

ฉันจะใช้ส่วนประกอบ GUI ได้อย่างไร =: S
Marc Rasmussen

1
@KevinMeredith ไม่ควรเรียกวิธีการเรียกของ Task บนเธรด FX - แต่ Task มีวิธีการบริดจ์ (updateProgress เป็นต้น) ที่รันบนเธรด FX ดู javadoc และบทช่วยสอนสำหรับข้อมูลเพิ่มเติม
assylias

12

ตอนนี้สามารถเปลี่ยนเป็นรุ่นแลมบ์ดาได้แล้ว

@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
หากคุณกำลังจัดการกับเหตุการณ์ GUI คุณอยู่ในเธรด GUI ทำไมคุณถึงใช้ runLater ()?
TFuto

ในขณะที่คุณพูดถูกคำตอบของ Caglar พยายามเน้นการใช้นิพจน์แลมบ์ดาไม่จำเป็นต้องเป็นตัวอย่างการเข้ารหัสที่ดี ฉันใช้Platform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin

2

เหตุผลหนึ่งในการใช้ Platform.runLater () อย่างชัดเจนอาจเป็นเพราะคุณผูกคุณสมบัติใน ui กับคุณสมบัติ service (ผลลัพธ์) ดังนั้นหากคุณอัปเดตคุณสมบัติบริการที่ถูกผูกไว้คุณต้องดำเนินการนี้ผ่าน runLater ():

ในเธรด UI หรือที่เรียกว่าเธรดแอ็พพลิเคชัน JavaFX:

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

ในการใช้บริการ (ผู้ปฏิบัติงานพื้นหลัง):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.