如何在JavaFX2中在Service和UI之间传递用户定义的对象和异常?

lik*_*udo 4 service worker javafx-2

如何在JavaFX2中的服务和UI之间传递用户定义的对象和用户定义的(已检查的)异常?这些示例仅显示将String作为属性发送到服务,并将可观察字符串数组发送回UI.

似乎仅为简单类型定义属性.StringProperty,IntegerProperty,DoubleProperty等.目前我有一个用户定义的对象(不是简单的类型),我希望Task操作并使用它生成的输出数据进行更新.我通过Service的构造函数发送它,它通过Task的构造函数传递它.我想知道参数必须通过属性传递的限制.此外,如果在Task的操作期间抛出异常,它将如何从Service传递到UI?我只看到一个getException()方法,没有传统的throw/catch.

属性http://docs.oracle.com/javafx/2/binding/jfxpub-binding.htm

服务与任务http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm

服务javadocs http://docs.oracle.com/javafx/2/api/javafx/concurrent/Service.html#getException()

"因为任务是专为使用JavaFX GUI应用程序的使用,它可以确保每次更改其公共属性,以及为国家,错误和事件处理程序的变更通知,所有发生的主要的JavaFX应用程序线程.访问这些属性从后台线程(包括call()方法)将导致引发运行时异常.

强烈建议使用任务运行的不可变状态初始化所有任务.这应该通过提供一个Task构造函数来完成,该构造函数接受执行Task所需的参数.不可变状态使得从任何线程使用变得容易和安全,并确保在存在多个线程时的正确性."

但是如果我的UI只在Task完成后触摸了对象,那么它应该没问题,对吧?

jew*_*sea 6

Service有一个签名Service<V>,它<V>是一个泛型类型参数,用于指定服务提供的任务中返回对象的类型.

假设您要定义一个返回Foo类型的用户定义对象的服务,那么您可以这样做:

 class FooGenerator extends Service<Foo> {
   protected Task createTask() {
     return new Task<Foo>() {
       protected Foo call() throws Exception {
         return new Foo();
       }
     };
   }
 }
Run Code Online (Sandbox Code Playgroud)

要使用该服务:

 FooGenerator fooGenerator = new FooGenerator();
 fooGenerator.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
   @Override public void handle(WorkerStateEvent t) {
     Foo myNewFoo = fooGenerator.getValue();
     System.out.println(myNewFoo);
   }
 });
 fooGenerator.start();
Run Code Online (Sandbox Code Playgroud)

如果要在每次启动或重新启动之前将输入值传递到服务中,则必须更加小心.您可以将要输入的值作为服务上的可设置成员添加到服务中.在调用服务的start方法之前,可以从JavaFX应用程序线程调用这些setter.然后,在创建服务的任务时,将参数传递给服务的Task的构造函数.

执行此操作时,最好使所有信息在线程之间来回传递不可变.对于下面的示例,Foo对象作为输入参数传递给服务,Foo对象基于作为服务输出接收的输入.但是Foo本身的状态只是在它的构造函数中初始化--Foo的实例是不可变的,一旦创建就无法更改,并且它的所有成员变量都是最终的并且不能改变.这使得更容易推理程序,因为您永远不必担心另一个线程可能同时覆盖该状态.它似乎有点复杂,但它确实使一切都非常安全.

class FooModifier extends Service<Foo> {
  private Foo foo;
  void setFoo(Foo foo) { this.foo = foo; }

  @Override protected Task createTask() {
    return new FooModifierTask(foo);
  }

  private class FooModifierTask extends Task<Foo> {
    final private Foo fooInput;
    FooModifierTask(Foo fooInput) { this.fooInput = fooInput; }

    @Override protected Foo call() throws Exception {
      Thread.currentThread().sleep(1000);
      return new Foo(fooInput);
    }
  }
}

class Foo {
  private final int answer;
  Foo()          { answer = random.nextInt(100); }
  Foo(Foo input) { answer = input.getAnswer() + 42; }
  public int getAnswer() { return answer; }
}
Run Code Online (Sandbox Code Playgroud)

还有一个向ServiceService javadoc中的 a提供输入的示例.


要从服务返回自定义用户异常,只需在服务的任务调用处理程序中抛出用户异常.例如:

 class BadFooGenerator extends Service<Foo> {
   @Override protected Task createTask() {
     return new Task<Foo>() {
       @Override protected Foo call() throws Exception {
         Thread.currentThread().sleep(1000);
         throw new BadFooException();
       }
     };
   }
 }
Run Code Online (Sandbox Code Playgroud)

并且可以像这样检索异常:

 BadFooGenerator badFooGenerator = new BadFooGenerator();
 badFooGenerator.setOnFailed(new EventHandler<WorkerStateEvent>() {
   @Override public void handle(WorkerStateEvent t) {
     Throwable ouch = badFooGenerator.getException();
     System.out.println(ouch.getClass().getName() + " -> " + ouch.getMessage());
   }
 });
 badFooGenerator.start();
Run Code Online (Sandbox Code Playgroud)

我创建了几个可执行的示例,您可以使用它来尝试这一点.