2017-12-28 13 views
0

私の目的は、Webサーバーからドキュメントを読み込んで、特定のコンテンツのDOMを解析することです。 DOMのロードは私の問題です。なぜWebEngineのワーカースレッドは完了しませんか?

javafx.scene.web.WebEngineを使用しようとしていますが、最終的なDOMに影響を与える可能性のあるjavascriptの実行を含む必要なすべての仕組みを行うことができるように見えます。

ドキュメントをロードするときにRUNNING状態になっていて、WebEngine.getDocument()からDOMにアクセスする前に、SUCCEEDED状態には達していないようです。

これは、URLまたはリテラルコンテンツ(この最小限の例で使用されている)からロードする場合に発生します。

誰かが私が間違っている、または誤解していることを見ることができますか?

ご協力いただきありがとうございます。

import java.util.concurrent.ExecutionException; 
import org.w3c.dom.Document; 
import javafx.application.Platform; 
import javafx.concurrent.Task; 
import javafx.concurrent.Worker; 
import javafx.embed.swing.JFXPanel; 
import javafx.scene.web.WebEngine; 

public class WebEngineProblem { 
    private static Task<WebEngine> getEngineTask() { 
     Task<WebEngine> task = new Task<>() { 
      @Override 
      protected WebEngine call() throws Exception { 
       WebEngine webEngine = new WebEngine(); 
       final Worker<Void> loadWorker = webEngine.getLoadWorker(); 
       loadWorker.stateProperty().addListener((obs, oldValue, newValue) -> { 
        System.out.println("state:" + newValue); 
        if (newValue == State.SUCCEEDED) { 
         System.out.println("finished loading"); 
        }  
       }); 
       webEngine.loadContent("<!DOCTYPE html>\r\n" + "<html>\r\n" + "<head>\r\n" + "<meta charset=\"UTF-8\">\r\n" 
        + "<title>Content Title</title>\r\n" + "</head>\r\n" + "<body>\r\n" + "<p>Body</p>\r\n" + "</body>\r\n" 
        + "</html>\r\n"); 
       State priorState = State.CANCELLED; //should never be CANCELLED 
       double priorWork = Double.NaN; 
       while (loadWorker.isRunning()) { 
        final double workDone = loadWorker.getWorkDone(); 
        if (loadWorker.getState() != priorState || priorWork != workDone) { 
         priorState = loadWorker.stateProperty().getValue(); 
         priorWork = workDone; 
         System.out.println(priorState + " " + priorWork + "/" + loadWorker.getTotalWork()); 
        } 
        Thread.sleep(1000); 
       } 
       return webEngine; 
      } 
     }; 
     return task; 
    } 

    public static void main(String[] args) { 
     new JFXPanel(); // Initialise the JavaFx Platform 
     WebEngine engine = null; 
     Task<WebEngine> task = getEngineTask(); 
     try { 
      Platform.runLater(task); 
      Thread.sleep(1000); 
      engine = task.get(); // Never completes as always RUNNING 
     } 
     catch (InterruptedException | ExecutionException e) { 
      e.printStackTrace(); 
     } 
     // This code is never reached as the content never completes loading 
     // It would fail as it's not on the FX thread. 
     Document doc = engine.getDocument(); 
     String content = doc.getTextContent(); 
     System.out.println(content); 
    } 

} 
+1

タスクがバックグラウンドスレッドで実行するように設計されています:あなたは、FXアプリケーションのスレッドでこのタスクを実行している

はここでの例では、私はあなたがやろうとしていると思う何をしています。 'SUCCEEDED'への状態変更は、FXアプリケーションスレッドでも発生しなければならないので、タスクが完了するまで状態を変更することはできません。あなたのwhileループは、 'loadWorker'が' RUNNING'ステートから抜けるまであなたが事実上デッドロックの奇妙な形を持つまで完了しません。 –

+0

確かに、 'Platform.runLater() '呼び出しはFXApplicationスレッド上でタスクを強制的に実行する方法ですが、ワーカースレッドは別々のものですか? webEngine.loadContent()はすぐに戻ります。そのため、ロードは別のワーカースレッドで発生している必要があります。 "バックグラウンドスレッドで常にローディングが発生するバックグラウンドジョブのスケジューリングの直後にロードを開始するメソッドが返される進行状況を追跡したり、ジョブを取り消したりするには、getLoadWorker()メソッドで使用可能なWorkerインスタンスを使用する。 https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebEngine.html – Dragonthoughts

+1

ワーカースレッドは別々のものですが、ワーカーの 'stateProperty() 'に対する実際の変更は、 'FXアプリケーションスレッドで発生する必要があります。 (基本的にこれらのプロパティはすべてシングルスレッドです。)したがって、ワーカースレッドの実装では、どこかに 'Platform.runLater(...) '状態を更新します。 FXアプリケーションスレッドをブロックした場合、その呼び出しは実際には起こりません。 (基本的に、「ヘッドレス」モードで実行していても、FXアプリケーションスレッドを決してブロックしないでください。) –

答えて

1

Workerstateプロパティへの変更は、その労働者がバックグラウンドスレッドで実行されているにもかかわらず、FXのアプリケーションスレッドで発生します。 (JavaFXプロパティは本質的にシングルスレッドです)。Webエンジンのコンテンツをロードするスレッドの実装のどこかに、Platform.runLater(...)があり、ワーカーの状態が変更されます。

ワーカーの状態が変更され、FXアプリケーションスレッドでタスクを実行するまで、タスクがブロックされるため、本質的にFXアプリケーションスレッドにデッドロックが発生しました。つまり、負荷ワーカの状態への変更は発生しませんタスクが完了するまで(同じスレッドで実行されているため)、状態が変更されるまでタスクを完了できません(タスクをプログラムしたとおりです)。

FXアプリケーションスレッドをブロックするのは、基本的には常にエラーです。代わりに、必要な条件が満たされるまで(Webエンジンが作成され、スレッドのロードが完了するまで)別のスレッドをブロックし、その次の処理を実行する必要があります(Platform.runLater(...) FXアプリケーションスレッド)。

import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.FutureTask; 

import org.w3c.dom.Document; 

import javafx.application.Platform; 
import javafx.concurrent.Worker; 
import javafx.concurrent.Worker.State; 
import javafx.embed.swing.JFXPanel; 
import javafx.scene.web.WebEngine; 

public class WebEngineProblem { 

    public static void main(String[] args) throws InterruptedException, ExecutionException { 
     new JFXPanel(); // Initialise the JavaFx Platform 

     CountDownLatch loaded = new CountDownLatch(1); 

     FutureTask<WebEngine> createEngineTask = new FutureTask<WebEngine>(() -> { 
      WebEngine webEngine = new WebEngine(); 
      final Worker<Void> loadWorker = webEngine.getLoadWorker(); 
      loadWorker.stateProperty().addListener((obs, oldValue, newValue) -> { 
       System.out.println("state:" + newValue); 
       if (newValue == State.SUCCEEDED) { 
        System.out.println("finished loading"); 
        loaded.countDown(); 
       }  
      }); 
      webEngine.loadContent("<!DOCTYPE html>\r\n" + "<html>\r\n" + "<head>\r\n" + "<meta charset=\"UTF-8\">\r\n" 
       + "<title>Content Title</title>\r\n" + "</head>\r\n" + "<body>\r\n" + "<p>Body</p>\r\n" + "</body>\r\n" 
       + "</html>\r\n"); 
      return webEngine ; 
     }); 

     Platform.runLater(createEngineTask); 
     WebEngine engine = createEngineTask.get(); 
     loaded.await(); 

     Platform.runLater(() -> { 
      Document doc = engine.getDocument(); 
      String content = doc.getDocumentElement().getTextContent(); 
      System.out.println(content); 
     }); 
    } 

} 
+0

ありがとうございます、本当の問題を解決するための素晴らしい基盤のようです:) – Dragonthoughts

関連する問題