Platform.runLater e Task em JavaFX

88

Tenho feito pesquisas sobre isso, mas ainda estou MUITO confuso, para dizer o mínimo.

Alguém pode me dar um exemplo concreto de quando usar Taske quando usar Platform.runLater(Runnable);? Qual é exatamente a diferença? Existe uma regra de ouro para quando usar qualquer um deles?

Também me corrija se eu estiver errado, mas esses dois "Objetos" não são uma maneira de criar outro thread dentro do thread principal em uma GUI (usado para atualizar a GUI)?

Marc Rasmussen
fonte

Respostas:

108

Use Platform.runLater(...)para operações rápidas e simples e Taskpara operações complexas e grandes.

Exemplo: Por que não podemos usar Platform.runLater(...)para cálculos longos (retirado da referência abaixo).

Problema: thread em segundo plano que conta apenas de 0 a 1 milhão e atualiza a barra de progresso na IU.

Código usando 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();

Este é um pedaço de código horrível, um crime contra a natureza (e a programação em geral). Primeiro, você perderá células cerebrais apenas olhando para este aninhamento duplo de Runnables. Em segundo lugar, vai inundar a fila de eventos com pequenos Runnables - um milhão deles na verdade. Claramente, precisávamos de alguma API para tornar mais fácil escrever workers em segundo plano que então se comunicam de volta com a UI.

Código usando tarefa:

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ão sofre de nenhuma das falhas exibidas no código anterior

Referência: Worker Threading em JavaFX 2.0

invariante
fonte
O exemplo de tarefa no link Ensemble App não funcionará mais.
Cugomastik
Este é o link correto, mas não posso editá-lo porque a edição tem apenas 2 caracteres.
Aerus
29
Dizer que um para operações "rápidas" e outro para operações "complexas" é um pouco enganador. A principal diferença, afinal, é que um permite executar o código no thread da GUI JFX de outro lugar, e o outro faz exatamente o oposto - permite executar o código no thread de segundo plano do thread da GUI (adicionando um bônus de ser capaz de se comunicar com o thread da GUI no processo).
Sergei Tachenov
Quero salvar uma imagem de cada uma das cenas - e isso vai levar algum tempo. Isso causará problemas quando executado em um thread separado por meio de Tarefa? Eu entendi que todo o trabalho relacionado ao gui precisa acontecer no thread FxApplication.
StephenBoesch
@ Sergey-Tachenov Então, você usa runLater () de um thread de tarefa para atualizar o thread da GUI nos casos em que deseja fazer mais do que apenas atualizar uma única propriedade, como progresso?
simpleuser
58
  • Platform.runLater: Se você precisar atualizar um componente GUI de um thread não GUI, você pode usar isso para colocar sua atualização em uma fila e ela será tratada pelo thread GUI o mais rápido possível.
  • Taskimplementa a Workerinterface que é usada quando você precisa executar uma tarefa longa fora do thread da GUI (para evitar o congelamento de seu aplicativo), mas ainda precisa interagir com a GUI em algum estágio.

Se você está familiarizado com o Swing, o primeiro é equivalente ao SwingUtilities.invokeLatere o último ao conceito de SwingWorker.

O javadoc de Task fornece muitos exemplos que devem esclarecer como eles podem ser usados. Você também pode consultar o tutorial sobre simultaneidade .

assilias
fonte
Obrigado. Você pode dar um pequeno exemplo de como usar a plataforma? Você pode usá-lo fora do thread gui ou? E os exemplos de tarefas nos documentos não são realmente claros
Marc Rasmussen
Sim, Platform.runLater pode ser usado fora do thread da GUI - esse é seu objetivo principal. Você pode achar este tutorial sobre tarefas mais informativo do que o javadoc.
Assylias
3
Você usa Platform.runLater assim:Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias
como poderei usar os componentes da GUI então =: S
Marc Rasmussen
1
@KevinMeredith O método de chamada do Task não deve ser chamado no thread FX - mas o Task fornece métodos de ponte (updateProgress etc.) que são executados no thread FX. Veja o javadoc e tutoriais para mais informações.
Assylias
12

Agora pode ser alterado para a versão 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);
        }
    });
}
Cugomastik
fonte
8
Se você estiver lidando com um evento GUI, você está no thread da GUI. Por que você usaria runLater () então?
TFuto
Embora você esteja certo, a resposta de Caglar foi tentar destacar o uso de uma expressão lambda, não necessariamente para dar um bom exemplo de codificação. Eu usoPlatform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin
2

Um motivo para usar um Platform.runLater () explícito pode ser que você vinculou uma propriedade na interface do usuário a uma propriedade de serviço (resultado). Portanto, se você atualizar a propriedade do serviço vinculado, terá que fazer isso via runLater ():

No thread de IU também conhecido como thread de aplicativo JavaFX:

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

na implementação do serviço (trabalhador em segundo plano):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
Alexander Pietsch
fonte