2017-01-27 11 views
6

現在、JavaFXベースのアプリケーションで作業しています。ユーザーは、ワールドマップ上にマークされた場所と対話できます。これを行うには、私はhttp://captaincasa.blogspot.de/2014/01/javafx-and-osm-openstreetmap.html([1])に記載されている方法と同様の方法を使用しています。ガベージコレクション後にJavascriptからJavaFx WebViewコールバックに失敗する

しかし、WebEngineのsetMember()メソッドを使用して埋め込みHTMLページに注入されるJavascriptコールバック変数に関連するデバッグが難しい問題に直面しています(公式チュートリアルではhttps://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/js-javafx.htm([2]も参照してください) 。

プログラムをしばらく実行すると、コールバック変数の状態が予期せず失われています。この動作を実証するために、最小限の作業/失敗の例を作成しました。私はWindows 10マシンで64ビットjdk1.8.0_121を使用しています。

次のようにJavaFXのアプリケーションが見えます:

import java.text.DateFormat; 
import java.text.SimpleDateFormat; 
import java.util.Date; 

import javafx.application.Application; 
import javafx.concurrent.Worker.State; 
import javafx.scene.Scene; 
import javafx.scene.layout.AnchorPane; 
import javafx.scene.web.WebEngine; 
import javafx.scene.web.WebView; 
import javafx.stage.Stage; 
import netscape.javascript.JSObject; 

public class WebViewJsCallbackTest extends Application { 

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); 

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

    public class JavaScriptBridge { 
     public void callback(String data) { 
      System.out.println("callback retrieved: " + data); 
     } 
    } 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     WebView webView = new WebView(); 
     primaryStage.setScene(new Scene(new AnchorPane(webView))); 
     primaryStage.show(); 

     final WebEngine webEngine = webView.getEngine(); 
     webEngine.load(getClass().getClassLoader().getResource("page.html").toExternalForm()); 

     webEngine.getLoadWorker().stateProperty().addListener((observableValue, oldValue, newValue) -> { 
      if (newValue == State.SUCCEEDED) { 
       JSObject window = (JSObject) webEngine.executeScript("window"); 
       window.setMember("javaApp", new JavaScriptBridge()); 
      } 
     }); 

     webEngine.setOnAlert(event -> { 
      System.out.println(DATE_FORMAT.format(new Date()) + " alerted: " + event.getData()); 
     }); 
    } 

} 

次のようにHTMLファイル "page.html" が見えます:

<!DOCTYPE html> 
<html> 
<head> 
<meta http-equiv="content-type" content="text/html; charset=UTF-8" /> 
<!-- use for in-browser debugging --> 
<!-- <script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> --> 
<script type="text/javascript"> 
    var javaApp = null; 

    function javaCallback(data) {   
     try { 
      alert("javaApp=" + javaApp + "(type=" + typeof javaApp + "), data=" + data); 
      javaApp.callback(data); 
     } catch (e) { 
      alert("caugt exception: " + e); 
     } 
    } 
</script> 
</head> 
<body> 
    <button onclick="javaCallback('Test')">Send data to Java</button> 
    <button onclick="setInterval(function(){ javaCallback('Test'); }, 1000)">Send data to Java in endless loop</button> 
</body> 
</html> 

コールバック変数javaAppの状態は上をクリックすることによって観察することができます"Send data to Java in endless loop"ボタンそれは、javaApp.callback経由でコールバックメソッドを継続的に実行しようとします。これにより、Javaアプリケーションにログメッセージが生成されます。アラートは、物事を裏返すための追加のコミュニケーションチャネルとして使用されます(常にうまくいくように見えますが、回避策として現在使用されていますが、それはどういうものなのか...)。

なってすべてが動作している場合は、次の行に似のログを記録するたびに印刷する必要があります。

callback retrieved: Test 
2017/01/27 21:26:11 alerted: [email protected]c693(type=object), data=Test 

しかし、しばらく(2-7分から何か)した後、これ以上のコールバックが取得されていません、しかし、次の行のようなだけloggingsが印刷されています

2017/01/27 21:32:01 alerted: javaApp=undefined(type=object), data=Test

を変数を印刷する今Javaインスタンス・パスの代わりに'undefined'を与えます。奇妙なことに、javaAppの状態は本当に「未定義」ではないということです。 typeofを使用するとobjectが返され、javaApp === undefinedfalseと評価されます。これは、コールバック呼び出しが例外をスローしないという事実に従っています(そうでなければ、"caugt exception: "で始まるアラートが出力されます)。

Java VisualVMを使用すると、障害の時間はガベージコレクタがアクティブになった時間と一致することがわかりました。これは、ヒープメモリ消費量を見ることでわかります。 GCにより60MB〜16MB。

ここには何がありますか?どのように問題をさらにデバッグすることができますか?私は関連するバグを見つけることができませんでした...

ありがとうございました!

PS:リーフレット(cf [1])を介して世界地図を表示するためのJavaScriptコードを含むと、問題がずっと速く再現されました。ほとんどの場合、地図をすぐにロードまたはシフトすると、GCがその仕事をしました。このオリジナルの問題をデバッグする際には、ここで紹介した最小限の例を挙げました。

+0

私はjdk 1.8.0_121と同じ問題がありました。それから、私はそれがjdk 1.8.0_60を実行している別のPCで動作することを認識しました。 1.8.0_60に切り替えられ、問題は解決されました。 –

+0

@AliAyadJalil:古いバージョンに落ちることは、必ずしも望ましくない可能性があります。 : -/ –

答えて

7

setMember()でJavascriptに送信されたJavaScriptBridgeインスタンスを保持するJavaでインスタンス変数bridgeを作成してこの問題を解決しました。このようにして、インスタンスのGargbage Collectionは防止されます。

関連するコードスニペット:コードAltough

public class JavaScriptBridge { 
    public void callback(String data) { 
     System.out.println("callback retrieved: " + data); 
    } 
} 

private JavaScriptBridge bridge; 

@Override 
public void start(Stage primaryStage) throws Exception { 
    WebView webView = new WebView(); 
    primaryStage.setScene(new Scene(new AnchorPane(webView))); 
    primaryStage.show(); 

    final WebEngine webEngine = webView.getEngine(); 
    webEngine.load(getClass().getClassLoader().getResource("page.html").toExternalForm()); 

    bridge = new JavaScriptBridge(); 
    webEngine.getLoadWorker().stateProperty().addListener((observableValue, oldValue, newValue) -> { 
     if (newValue == State.SUCCEEDED) { 
      JSObject window = (JSObject) webEngine.executeScript("window"); 
      window.setMember("javaApp", bridge); 
     } 
    }); 

    webEngine.setOnAlert(event -> { 
     System.out.println(DATE_FORMAT.format(new Date()) + " alerted: " + event.getData()); 
    }); 
} 

は今(リーフレットと一緒にも)スムーズに動作しますが、私はまだ、この予期しない動作のイライラしています...

それはより多くのAのように感じているので、回避策は、なぜこの動作が期待されるべきか、正しいのかを誰かが説明できない限り、正解とはみなされません。

+0

参照:https://bugs.openjdk.java.net/browse/JDK-8154127 –

+0

ありがとうございましたあなたのコードは私の日を節約しました、受け入れられるはずです – Dinesh

+0

OMG。どのようなひどいHeisenbug。この答えは100万に感謝します。私は問題の原因を探して何日も過ごしました。 –

関連する問題