2014-09-16 6 views
14

Considerate questo codice:Le attività di JavaFX sembrano consumare eccezioni. È un bug o una funzionalità?

Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> { 
    System.out.println("An exception occurred!"); 
}); 

// set the exception handler for the JavaFX application thread 
Thread.currentThread().setUncaughtExceptionHandler((Thread t, Throwable e) -> { 
    System.out.println("An exception occurred!"); 
}); 

Task<?> task = new Task() { 
    @Override 
    public Void call() throws Exception { 
     throw new RuntimeException("foobar"); 
    }; 
}; 

new Thread(task).start(); 

Se corriamo il codice di eccezione di runtime non fa scattare il gestore di eccezioni di default, ma è invece consumato dal compito. L'unico modo per contrastare questo che ho trovato è quello di rigenerare l'eccezione in task.setOnFailed:

task.setOnFailed((WorkerStateEvent t) -> { 
    throw new RuntimeException(task.getException()); 
}); 

Dal JavaFX 8 ora ha il supporto per l'UncaughtExceptionHandler perché non è l'eccezione propagato al gestore di eccezioni?

risposta

3

Feature, Task mantiene una proprietà exception. L'idea è che quando un'eccezione viene lanciata, le attività falliscono e si può chiedere quale eccezione è stata lanciata. Sotto questo aspetto Task è stato concepito come un lavoro quasi in batch, eseguito in background e, eventualmente, in mancanza di successo.

Ciò riflette anche un po 'del comportamento asincrono; dove l'eccezione potrebbe essere gestita. Non nel luogo in cui è stato chiamato start.

+0

C'è un modo per rilanciare l'eccezione senza avvolgerla in una RuntimeException? Ho provato 'throw task.getException();' ma il compilatore ha appena urlato. – user555

+0

Non per la mia conoscenza molto limitata su JavaFX. –

+1

No, non puoi. Come puoi vedere nella documentazione (https://docs.oracle.com/javafx/2/api/javafx/event/EventHandler.html), il metodo di gestione dell'Eventhandler non genera alcuna eccezione.Inoltre, non è possibile assegnare l'Eccezione al blocco circostante per rethrow, perché quel blocco è già stato abbandonato a causa della chiamata asincrona. –

5

All'interno del metodo Task.call() semplicemente gettare l'eccezione e aggiungere un ChangeListener al compito in questo modo:

task.exceptionProperty().addListener((observable, oldValue, newValue) -> { 
    if(newValue != null) { 
    Exception ex = (Exception) newValue; 
    ex.printStackTrace(); 
    } 
}); 

Poi, dopo l'operazione non è riuscita con l'eccezione, si ottiene una notifica da chi ascolta, che è stato gettato un'eccezione durante l'esecuzione. È possibile scambiare facilmente la riga ex.printStackTrace(); con un Alert se si è nel thread di esecuzione JavaFX.

2

So che questa domanda è vecchia, ma ho cercato una risposta e non ho trovato molto su questo argomento. Penso che tu possa aggiungere a l'eccezione se ti piace farlo. Ecco il codice di esempio:

public class Main extends Application { 

@Override 
public void start(Stage stage) { 

    Task<Void> task = new Task<Void>() { 
     @Override 
     protected Void call() throws Exception { 
      throw new IndexOutOfBoundsException(); 
     } 
    }; 
    task.setOnSucceeded(evt -> System.out.println("Task succeeded!")); 
    task.setOnCancelled(evt -> System.out.println("Task cancelled!")); 
    task.setOnFailed(evt -> { 
     System.out.println("Task failed!"); 
     if (task.getException() instanceof IndexOutOfBoundsException) { 
      System.out.println("...with an IndexOutOfBoundsException"); 
     } else if (task.getException() instanceof NumberFormatException) { 
      System.out.println("...with a NumberFormatException"); 
     } else { 
      System.out.println("...with another, unexpected execption"); 
     } 
    }); 

    VBox box = new VBox(); 
    Scene scene = new Scene(box, 200, 200); 
    stage.setScene(scene); 
    stage.setTitle("Thread Example"); 
    stage.show(); 

    new Thread(task).start(); 

} 

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

Console-Output: attività non riuscita! ... con un IndexOutOfBoundsException

Se viene generata un'eccezione all'interno di un compito, allora il compito finisce in stato di 'non riuscita'. Nel metodo setOnFailed è possibile gestire l'errore dell'attività. Tutto il codice all'interno di questo metodo si trova nel thread dell'applicazione di JavaFX ma è possibile fare affidamento sull'eccezione dell'attività da task.getException(). Inoltre, questo codice funziona solo con JavaFX (ho cercato di ottenere lo stesso output in una normale app java, ma non ha funzionato).

+1

nessuna buona soluzione perché devo "indovinare" l'eccezione ... ok, se conosci il tuo codice, probabilmente sai qual è l'eccezione, ma non è sempre così. – ConquerorsHaki

3

un po 'tardi, ma probabilmente è possibile stampare il throwable stesso:

task.setOnFailed(new EventHandler<WorkerStateEvent>() { 
    @Override 
    public void handle(WorkerStateEvent arg0) { 
     Throwable throwable = task.getException(); 
     throwable.printStackTrace(); 
    } 
} 

Le eccezioni si butta in ogni caso, ma è possibile utilizzare questo per visualizzare all'utente o accedere esso.