我正在学习JavaFX,并且遇到了运行任务的绊脚石。我使用NetBeans调试器跟踪了任务的进度,发现ConfigModelSansUpdateTask
明确运行。
当应用程序到达configModelWorkerSansUpdates.setOnSucceeded
时,此结果(存储在configModel
中)会以null
变为NullPointerException
,并抛出NullPointerException
。
阅读了/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package swmuapp;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import jaxb.TaskModel;
/**
*
* @author Shaun Connelly-Flynn
*/
public class SWMUApp extends Application {
public SWMUApp() {
}
@Override
public void start(Stage stage) throws Exception {
FXMLLoader centralSeceneLoader = new FXMLLoader(getClass().getResource("forms/CentralScene.fxml"));
Parent centralRoot = (Parent) centralSeceneLoader.load();
Scene scene = new Scene(centralRoot);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
上的帖子后,我对于为什么我的任务结果被取消没有明智之处。没有什么可以做到这一点,因为当我使用它时,我明显得到一个结果然后变为null:
任务即将返回结果的快照:
当我来使用结果时:
帮助!
SWMUApp.java
package swmuapp.controllers;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.concurrent.WorkerStateEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Tab;
import jaxb.TaskModel;
import swmuapp.SWMUApp;
import swmuapp.init.ConfigModelWorkerSansUpdates;
import swmuapp.init.LocationModelWorker;
import swmuapp.models.ConfigModel;
import swmuapp.models.LocationModel;
/**
* FXML Controller class
*
* @author Shaun Connelly-Flynn
*/
public class CentralSceneController implements Initializable {
@FXML
private Tab sessionTab;
@FXML
private Tab tractionTab;
@FXML
private Tab locationTab;
@FXML
private Tab swmuTab;
@FXML
private Tab exportTab;
private final TaskModel taskModel;
// Models
private ConfigModel configModel;
private LocationModel locationModel;
// Child Controllers
@FXML
private ImportPaneController importPaneController;
@FXML
private SWMUPaneController swmuPaneController;
@FXML
private SessionPaneController sessionPaneController;
public CentralSceneController() {
this.taskModel = new TaskModel();
}
/**
* Initialises the controller class.
*
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
// Give a reference of this controller to the child controllers
importPaneController.setParentController(this);
// Now subimt the configModel task
ConfigModelWorkerSansUpdates configModelWorkerSansUpdates = new ConfigModelWorkerSansUpdates(taskModel);
configModelWorkerSansUpdates.setOnSucceeded((WorkerStateEvent event) -> {
System.out.println(event.getEventType().toString());
// Get the location model
Future<LocationModel> locationFuture = taskModel.submitTask(new LocationModelWorker(configModel, taskModel));
try {
locationModel = locationFuture.get();
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(SWMUApp.class.getName()).log(Level.SEVERE, null, ex);
}
// Notify the controllers that we're ready
sessionPaneController.setConfigModel(configModel);
sessionPaneController.setLocationModel(locationModel);
});
configModelWorkerSansUpdates.setOnFailed(p -> System.out.println(p.toString()));
configModelWorkerSansUpdates.setOnCancelled(p -> System.out.println(p.toString()));
// Fetch the config model
Future<ConfigModel> configFuture = taskModel.submitTask(configModelWorkerSansUpdates);
try {
configModel = configFuture.get();
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(SWMUApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
CentralSceneController.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package jaxb;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javafx.concurrent.Task;
/**
*
* @author Shaun Connelly-Flynn
*/
public class TaskModel {
private final ExecutorService service;
public TaskModel() {
this.service = Executors.newCachedThreadPool();
}
public Future submitTask(Callable task) {
return service.submit(task);
}
public Future submitTask(Task task) {
return service.submit(task);
}
public List<Future> submitTasks(List<Callable> tasks) {
List<Future> futureList = new ArrayList<>();
for(Callable task : tasks) {
futureList.add(submitTask(task));
}
return futureList;
}
}
TaskModel.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package swmuapp.init;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javafx.concurrent.Task;
import jaxb.Binder;
import jaxb.TaskModel;
import jaxb.bundles.QueryBundle;
import jaxb.bundles.ResultsBundle;
import jaxb.query.XQuery;
import swmuapp.config.jaxb.JaxbConfig;
import swmuapp.config.jaxb.JaxbDatabases;
import swmuapp.models.ConfigModel;
/**
*
* @author Shaun Connelly-Flynn
*/
public class ConfigModelWorkerSansUpdates extends Task<ConfigModel> {
private final TaskModel taskModel;
public ConfigModelWorkerSansUpdates(TaskModel taskModel) {
this.taskModel = taskModel;
}
@Override
protected ConfigModel call() throws Exception {
// Get the list of active databases
QueryBundle databaseBundle = new QueryBundle(
new XQuery("SWMUDB", "<databases>{databases/database}</databases>"),
JaxbDatabases.class,
"src/swmuapp/schema/DatabaseSchema.xsd");
// Now read the configuration
QueryBundle configBundle = new QueryBundle(
new XQuery("SWMUDB", "configs/config"),
JaxbConfig.class,
"src/swmuapp/schema/ConfigSchema.xsd");
// Submit the tasks
Future<ResultsBundle> dbTask = taskModel.submitTask(new Binder(databaseBundle, taskModel));
Future<ResultsBundle> configTask = taskModel.submitTask(new Binder(configBundle, taskModel));
// Create empty objects in case of failure!
JaxbDatabases dbResult = new JaxbDatabases();
JaxbConfig configResult = new JaxbConfig();
try {
// Now fetch the list of databases
dbResult = (JaxbDatabases) dbTask.get().getResult();
// Fetch the configuration
configResult = (JaxbConfig) configTask.get().getResult();
} catch (ExecutionException ex) {
setException(ex);
throw ex;
}
// Now put it all together
ConfigModel configModel = new ConfigModel(dbResult);
// And give it back!
return configModel;
}
}
ConfigModelWorkerSansUpdates.java
package swmuapp.controllers;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
import javafx.scene.control.TableView;
import swmuapp.models.ConfigModel;
import swmuapp.models.LocationModel;
/**
* FXML Controller class
*
* @author Shaun Connelly-Flynn
*/
public class SessionPaneController implements Initializable {
@FXML
private ListView<String> dbListView;
@FXML
private TableView<?> detailedTableView;
private CentralSceneController controller;
private ConfigModel configModel;
private LocationModel locationModel;
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
}
public void setConfigModel(ConfigModel configModel) {
if (this.configModel == null) {
this.configModel = configModel;
} else {
throw new IllegalArgumentException("SessionPaneController::setConfigModel -- config model already set!");
}
dbListView.setItems(configModel.getDatabases());
}
public void setLocationModel(LocationModel locationModel) {
this.locationModel = locationModel;
}
protected void setParentController(CentralSceneController controller) {
this.controller = controller;
}
}
SessionPaneController.java
java.util.concurrent.ExecutionException: java.lang.NullPointerException
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at swmuapp.controllers.CentralSceneController.lambda$initialize$0(CentralSceneController.java:84)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219)
at javafx.concurrent.Task.fireEvent(Task.java:1356)
at javafx.concurrent.Task.setState(Task.java:723)
at javafx.concurrent.Task$TaskCallable.lambda$call$500(Task.java:1434)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at swmuapp.init.LocationModelWorker.call(LocationModelWorker.java:35)
at swmuapp.init.LocationModelWorker.call(LocationModelWorker.java:22)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 more
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at swmuapp.controllers.SessionPaneController.setConfigModel(SessionPaneController.java:49)
at swmuapp.controllers.CentralSceneController.lambda$initialize$0(CentralSceneController.java:90)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219)
at javafx.concurrent.Task.fireEvent(Task.java:1356)
at javafx.concurrent.Task.setState(Task.java:723)
at javafx.concurrent.Task$TaskCallable.lambda$call$500(Task.java:1434)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
堆栈:
public function store(Request $request){
$input = Input::all();
$servicedata = $input['service_id'];
$employeedata = $input['emp_id'];
$allOrder = array();
foreach ($employeedata as $key => $val) {
$detailorder = new DetailOrder;
$detailorder->employee_id = $input['emp_id'][$key];
$detailorder->mandays = $input['mandays'][$key];
foreach ($servicedata as $key => $val) {
$detailorder->sales_order_id = $request->sales_order_id;
$detailorder->serivce_id = $input['service_id'][$key];
$detailorder->order_type = $input['order_type'][$key];
$detailorder->select_plan = $input['select_plan'][$key];
$detailorder->qty = $input['qty'][$key];
$detailorder->unit_price = $input['unit_price'][$key];
$detailorder->note = $input['note'][$key];
}
$detailorder->save();
}
}
答案 0 :(得分:1)
我怀疑调度决策的某些组合导致onSucceeded
处理程序在Future.get
返回之前执行,因此尚未分配CentralSceneController.configModel
。
在从同一个线程发布任务后直接使用Future.get()
是不行的,因为它强制当前线程等待任务完成,并且可能已经用这段时间直接运行ask。如果不应该阻止当前线程,比如javafx应用程序线程,情况会更糟。
解决这个问题应该很容易:
只需从onSucceeded
处理程序中检索值,因为在应用程序线程上任务完成后触发此处理程序并使用此方法检索该值不会阻止应用程序线程。 / p>
以下代码假定LocationModelWorker
扩展Task<LocationModel>
重写LocationModelWorker
以Future<ConfigModel>
作为constuctor参数而不是ConfigModel
参数。在get
方法启动之前,请确保Future
未call
被调用(例如,不要从构造函数中调用它)。
configModelWorkerSansUpdates.setOnSucceeded((WorkerStateEvent event) -> {
sessionPaneController.setConfigModel(configModelWorkerSansUpdates.getValue());
});
...
Future<ConfigModel> configFuture = taskModel.submitTask(configModelWorkerSansUpdates);
LocationModelWorker locationModelWorker = new LocationModelWorker(configFuture, taskModel);
locationModelWorker.setOnSucceeded(evt -> {
sessionPaneController.setLocationModel(locationModelWorker.getValue());
});
...
taskModel.submitTask(locationModelWorker);
如果两个任务成功完成,您可能需要稍微调整一下以防止setLocationModel
和setConfigModel
。
答案 1 :(得分:0)
我从另一个帖子中找到了更多信息! Task
是Runnable
而非Callable
如本文所述:
FX Task, returning a value always returns null
Javadoc表明:
Future的get方法在成功完成后将返回null。
请参阅here以获取上述摘录的Javadoc。
我错误地假设Task
是Callable
的实现,而事实并非如此。
因此,Future.get()
在成功完成时将返回null,因为Runnable
没有返回结果。