从JavaFXPorts Webview访问本机android.webkit.WebView实例

时间:2017-07-13 13:58:45

标签: android android-webview javafx-8 javafxports javafx-webengine

我想通过

实现java-to-javascript方法调用

WebView.getEngine().executeScript("browserMessagingServicePush('Push data from Java to JS')");

在Android上使用javafxports 8.60.9,但得到了

java.lang.UnsupportedOperationException: Not supported yet.
...
at javafx.scene.web.WebEngine.executeScript(WebEngine.java:860)

异常。

如何引用Android的原生WebView来实现类似的东西?

WebView.addJavascriptInterface(new JSInterface(), "JSInterface");

还是有其他一些解决方法?

2 个答案:

答案 0 :(得分:0)

<强>解

我找到了一个使用Android原生WebView(而不是使用JavaFX WebView)从JavaFXPorts应用程序执行JavaScript的解决方案:

public class AndroidNativeService extends Activity implements NativeService {
    private Stage stage;

    public void myWebview(Stage stage) {
        this.stage = stage;

        FXActivity.getInstance().runOnUiThread(new Runnable() {     
            @Override
            public void run() {             
                WebView webView = new WebView(FXActivity.getInstance().getApplicationContext());
                FXActivity.getInstance().setContentView(webView);
                webView.getSettings().setJavaScriptEnabled(true);
                webView.getSettings().setDomStorageEnabled(true);
                webView.setWebViewClient(webClient);
                webView.loadUrl("http://www.example.com/index.html");
            }
        });
    }

    WebViewClient webClient = new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            // super.onPageFinished(view, url);          
            System.out.println(">>>>>>>>>>>>>>>>>>>>> onpagefinished title = "+view.getTitle());

            view.evaluateJavascript("(function(){return document.documentElement.outerHTML})();", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String html) {
                    System.out.println(">>>>>>>>>>>>>>>>>>>>> evaluateJavascript html = "+html);
                    // view.goBack();
                    // stage.show(); # get error java.lang.IllegalStateException: Not on FX application thread; currentThread = main
                }
            });
        }
    };

}

我使用以下方法调用此类:

Task<Void> task = new Task<Void>() {
    @Override 
    protected Void call() throws Exception {
        Platform.runLater( () ->  {
            GoNativePlatform goNativePlatform =  GoNativePlatformFactory.getPlatform();
            Looper.prepare(); // if don't use this then get error Looper.prepare() not set
            goNativePlatform.getNativeService().myWebView(stage);   
        });
        return null;
    }
};

Thread th = new Thread(task);
th.start();

我复制了来自Handling Android Native Activities like a Pro的类声明中声明的“Implements NativeService”,尽管这不是绝对必要的。 尝试使用此JavaFX WebView代码实现JavaFX WebView(而不是Android Native WebView)时:

Document doc = webEngine.getDocument();
getTitle(webView.getEngine());
String html = (String) webView.getEngine().executeScript("document.documentElement.outerHTML");

我在Android上运行时获得了空值或“不支持”,尽管代码在桌面上运行正常。 (还没有尝试过IOS,但我想在这里实现)。 我想检索网页标题并执行javascript以从网页中检索其他数据。 所以我的解决方案是在Android上运行时使用Android Native WebView而不是JavaFX WebView。 这是我的完整代码:

public class AndroidNativeService extends Activity implements NativeService {
    private Stage stage;

    public void myWebview(Stage stage) {
        this.stage = stage;

        FXActivity.getInstance().runOnUiThread(new Runnable() {     
            @Override
            public void run() {             
                WebView webView = new WebView(FXActivity.getInstance().getApplicationContext());
                FXActivity.getInstance().setContentView(webView);
                webView.getSettings().setJavaScriptEnabled(true);
                webView.getSettings().setDomStorageEnabled(true);
                webView.setWebViewClient(webClient);
                webView.loadUrl("http://www.example.com/index.html");

                webView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        System.out.println(">>>>>>>>>>>>>>>>>>>>> onClick()");
                        // webView.goBack();
                    }
                }); 
            }
        });
    }

    WebViewClient webClient = new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            // super.onPageFinished(view, url);          
            System.out.println(">>>>>>>>>>>>>>>>>>>>> onpagefinished title = "+view.getTitle());

            view.evaluateJavascript("(function(){return document.documentElement.outerHTML})();", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String html) {
                    System.out.println(">>>>>>>>>>>>>>>>>>>>> evaluateJavascript html = "+html);
                    // view.goBack();
                    // stage.show(); # get error java.lang.IllegalStateException: Not on FX application thread; currentThread = main
                }
            });
        }
    };

    @Override
    public void onBackPressed() {
        System.out.println(">>>>>>>>>>>>>>>>>>>>> Back Button Pressed");
        setResult(RESULT_CANCELED);
        super.onBackPressed();
        // FXActivity.getInstance().finish();
    }

}

虽然我已经成功显示了所需的网页,检索了标题并执行了一些JavaScript,但我仍在努力关闭网页并返回JavaFX阶段。 我在onBackPressed()OnClickListener之上的2个回调不起作用且未被调用(无法看到System.out.println)。你可以从上面看到我正在努力使用这些替代品:

  • webView.goBack();
  • stage.show(); // get error java.lang.IllegalStateException: Not on FX application thread; currentThread = main
  • FXActivity.getInstance().finish();

但我只是设法终止整个应用程序而不是返回JavaFX阶段(即使我将它的值传递给这个类)。有什么想法吗?

PS Looper.prepare();是必须能够调用此代码,虽然我不明白我在这里做什么?

答案 1 :(得分:0)

解决方案更新

我找到了一个使用Android原生WebView(而不是使用JavaFX WebView)从JavaFXPorts应用程序执行JavaScript的解决方案 - 请参阅下面的代码。 我通过使用2个活动解决了导航到我的JavaFX阶段和Android WebView的导航(在2018年2月25日的第一个回答中):我的主要活动(JavaFX Stage)调用第二个活动(Android WebView)并传递我想要的URL显示,当第二个Activity完成时,它会传回一个结果字符串。您可以通过执行finish()或使用后退按钮退出第二个Activity。您需要在AndroidManifest.xml中为第二个活动添加一个额外的条目。

这是我的主要活动:

import android.content.Intent;
import javafx.application.Platform;
import javafxports.android.FXActivity;

/*****************************************
   This is the main activity
******************************************/
public class MainActivity extends FXActivity {

    private static final int SECOND_ACTIVITY_REQUEST_CODE = 0;

    protected void webView() {

        Platform.runLater( () -> {
                String url ="http://www.example.com";
                Intent intent = new Intent(FXActivity.getInstance().getApplicationContext(), Test.class);
                intent.putExtra("url", url);

                // Add result handler
                FXActivity.getInstance().setOnActivityResultHandler((requestCode, resultCode, data) ->
                    switch (requestCode) {
                        case SECOND_ACTIVITY_REQUEST_CODE:
                             //if (resultCode == RESULT_OK) {}

                             // get String data from Intent
                             String result = data.getStringExtra("result");
                             System.out.println("result = " + result);
                             break;

                        default:                             
                             super.onActivityResult(requestCode, resultCode, data);
                             break;
                     }
                });

                FXActivity.getInstance().startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE);
        });

    }
}

这是我的第二个活动(Android WebView):

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.webkit.WebViewClient;

/*****************************************
    This is the 2nd activity
******************************************/
public class Test extends Activity {
    private String url;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            url = bundle.getString("url");
        }
        WebView webView = new WebView(this);
        setContentView(webView);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setDomStorageEnabled(true);
        webView.setWebViewClient(webClient);
        webView.loadUrl(url);
    }


    WebViewClient webClient = new WebViewClient() {

    @Override
    public void onPageFinished(WebView view, String url) {
         String title = view.getTitle();
         System.out.println("onpagefinished title = "+view.getTitle());

         view.evaluateJavascript("(function(){return document.documentElement.outerHTML})();",
                  new ValueCallback<String>() {

                      @Override
                      public void onReceiveValue(String html) {
                        System.out.println("evaluateJavascript html = "+html);

                        if (<exit 2nd activity condition>) {
                            // put the String to pass back into an Intent and close this activity
                            Intent intent = new Intent();
                            intent.putExtra("result", "this is the result string");
                            setResult(RESULT_OK, intent);
                            finishAndRemoveTask();                  // takes you back to main activity
                        }
                      }
             });
    }
    };

}

这是我的AndroidManifest.xml,我手动更改为Test.class添加第二个Activity:

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example" android:versionCode="1" android:versionName="1.0">
    <supports-screens android:xlargeScreens="true"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="21"/>
    <application android:label="APP" android:name="android.support.multidex.MultiDexApplication" android:icon="@mipmap/ic_launcher">
            <activity android:name="javafxports.android.FXActivity" android:label="Main" android:configChanges="orientation|screenSize">
                    <meta-data android:name="main.class" android:value="com.example.Launcher"/>
                    <meta-data android:name="debug.port" android:value="0"/>
                    <intent-filter>
                            <action android:name="android.intent.action.MAIN"/>
                            <category android:name="android.intent.category.LAUNCHER"/>
                    </intent-filter>
            </activity>
            <activity
                android:name="com.example.Test"
                android:label="Test"
                android:configChanges="orientation|screenSize">
            </activity>
    </application>
</manifest>

实例化MainActivity时,它会崩溃“无法创建没有Looper.prepare()的句柄”。未显式创建句柄,因此不清楚引用了哪个句柄。解决方案:创建一个looper然后再创建 实例化破坏它。只有在崩溃时才能发出Looper.prepare()。对MainActivity.webView()的后续调用在没有Looper的情况下工作。这是我的代码:

Looper.prepare();
new MainActivity();
Looper.myLooper().quitSafely();