我试图围绕javafx中的Service / Task以及如何将它们嵌套在一起。我的应用程序是一个简单的rss下载程序。它会下载多个Feed,并且还会在每个Feed项<link>
中下载html。我希望整个下载过程是异步的(以防止GUI冻结),以及每个rss feed下载和每个html下载。我希望这个过程看起来像这样。
Application thread.
|
|
|--------Download process start(Service)
| |
| |
| |----RSS download start(Service)
| | |(30+ Tasks that each download an individual feed.
| |----RSS download end
| |
| |
| |----HTML download start(Service)
| | |(100+ Tasks that each download an individual HTML page.
| |----HTML download end
| |
| |
|--------Download process end.
|
|
我的代码。 downloadStart()启动了Downloader
服务。
@Override
public void downloadStart(List<Channel> channels) {
Downloader downloader = new Downloader(channels);
downloader.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
List<Article> result = (List<Article>)t.getSource().getValue();
display.printToOutput("Completed download process : " + result.size());
}
});
downloader.start();
}
Downloader
班。
public class Downloader extends Service<List<Article>> {
List<Channel> channels;
public Downloader(List<Channel> channels){
this.channels = channels;
}
public void downloadRSS() {
for(Channel channel : channels){
RSSDownloadService<List<Article>> downloader = new RSSDownloadService<List<Article>>(channel);
downloader.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
List<Article> result = (List<Article>)t.getSource().getValue();
downloadHTML(result);
}
});
downloader.start();
}
}
private void downloadHTML(List<Article> articles){
HTMLDownloadService<List<Article>> downloader = new HTMLDownloadService<List<Article>>(articles);
downloader.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
List<Article> result = (List<Article>)t.getSource().getValue();
//how do i tell the Downloader service to return this result?
}
});
downloader.start();
}
@Override
protected Task<List<Article>> createTask() {
return new Task<List<Article>>() {
protected List<Article> call() {
downloadRSS();
//i can't return anything until downloadHTML() finishes!!
}
};
}
}
问题:启动Downloader服务后,它的createTask()
方法调用downloadRSS()
并期望返回值。但是,downloadRSS()
方法不会返回任何内容,它会启动RSSDownloadService
。当RSSDownloadService
成功时,它会调用downloadHTML(),它会启动HTMLDownloadService
。最后,当成功时,我想结束整个Downloader
服务并返回List
篇文章。我不知道该怎么办。
RSSDownloadService
和HTMLDownloadService
工作正常。它们对我来说很简单,因为它们调用一个带有返回值的方法。但是`DownloaderService&#39;不知何故需要等待2个服务完成,并返回第2个服务成功值。
答案 0 :(得分:1)
如果我理解正确,您希望downloadRSS()
返回List<Article>
,其中包含Article
生成的HTMLDownloadService
返回的所有列表中的所有public List<Article> downloadRSS() {
List<Article> mainList = Collections.synchronizedList(new ArrayList<>());
CountDownLatch latch = new CountDownLatch(channels.size());
for(Channel channel : channels){
RSSDownloadService<List<Article>> downloader = new RSSDownloadService<List<Article>>(channel);
downloader.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
List<Article> result = (List<Article>)t.getSource().getValue();
downloadHTML(result, mainList, latch);
}
});
downloader.setOnFailed(t -> {
// handle error if neccessary...
latch.countDown();
});
downloader.start();
}
latch.await();
// return a regular list, don't need the overhead of synchronization any more:
return new ArrayList<>(mainList);
}
private void downloadHTML(List<Article> articles, List<Article> mainList, CountDownLatch latch){
HTMLDownloadService<List<Article>> downloader = new HTMLDownloadService<List<Article>>(articles);
downloader.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
List<Article> result = (List<Article>)t.getSource().getValue();
mainList.addAll(result);
latch.countDown();
}
});
downloader.setOnFailed(t -> {
// handle error if needed...
latch.countDown();
});
downloader.start();
}
@Override
protected Task<List<Article>> createTask() {
return new Task<List<Article>>() {
protected List<Article> call() {
return downloadRSS();
}
};
}
。< / p>
我认为以下是您想要的:
public class Downloader extends Task<List<Article>> {
private final List<Channel> channels ;
public Downloader(List<Channel> channels) {
this.channels = channels ;
}
@Override
public List<Article> call() throws Exception {
return channels.parallelStream()
.flatMap(channel -> getRssList(channel).parallelStream())
.flatMap(rss -> getHtmlList(rss).stream())
.collect(Collectors.toList());
}
private List<Article> getRssList(Channel channel) {
// this runs in its own thread, return List<Article> for given channel
}
private List<Article> getHtmlList(Article rss) {
// this runs in its own thread, return List<Article> for given rss
}
}
这可能是一种更好的方法,使用Java 8流和内置并行化来管理大部分线程:
List<Channel> channels = ... ;
Downloader downloader = new Downloader(channels);
downloader.setOnSucceeded(e -> {
List<Article> articles = downloader.getValue();
// update UI with articles...
});
Thread t = new Thread(downloader);
t.setDaemon(true) ; // will not prevent application exit...
t.start();
然后你在ui中所需要的只是:
var config = require('../config');
var AWS = require('aws-sdk');
AWS.config.update({
accessKeyId: config.AWS_accessKeyId,
secretAccessKey: config.AWS_secretAccessKey,
region: 'eu-west-1'
});
var s3 = new AWS.S3();
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
exports.generateVideoThumbnail = function(fileId, url, done) {
var params = {
Bucket: config.AWS_bucket,
Key: url
};
var input = s3.getObject(params);
var stream = fs.createWriteStream('./screenshot.png');
return ffmpeg(input).screenshots({
timestamps: ['0.1', '0.2'],
size: '200x200'
}).output('./screenshot.png').output(stream).on('error', function(err) {
return done(err);
}).on('end', function() {
input.close();
var _key = "files/" + fileId + "/thumbnail.png";
return s3.putObject({
Bucket: config.AWS_bucket,
Key: _key,
Body: stream,
ContentType: 'image/jpeg'
}, function(err) {
return done(err);
});
});
};