从 JavaFXPorts Webview 访问本机 android.webkit.WebView 实例
Access to native android.webkit.WebView instance from JavaFXPorts Webview
我想通过
实现 java-to-java 脚本方法调用
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");
或者有其他解决方法?
解决方案
我找到了从 JavaFXPorts 应用程序使用 Android 本机 WebView(而不是使用 JavaFX WebView)执行 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
}
});
}
};
}
我调用此 class 使用:
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 的 class 声明中复制了 "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 上的 运行 时,我得到了空值或 "not supported yet",尽管代码在桌面上运行正常。 (还没有在 IOS 上尝试过,但我也想在这里实现)。
我想检索网页标题并执行 javascript 从网页中检索其他数据。
因此,我的解决方案是在 Android 上 运行 时使用 Android 本机 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,但我正在努力关闭网页并return到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();
但我只是设法终止整个应用程序,而不是 return 到 JavaFX 阶段(即使我将它的值传递给此 class)。有什么想法吗?
PS:Looper.prepare();
是调用此代码所必需的,尽管我不明白我在这里做什么?
解决方案更新
我找到了从 JavaFXPorts 应用程序使用 Android 本机 WebView(而不是使用 JavaFX WebView)执行 JavaScript 的解决方案 - 请参阅下面的代码。
我通过使用 2 个活动解决了往返 JavaFX 阶段和 Android WebView(在我 2018 年 2 月 25 日的第一个答案中)的导航问题:我的主要 activity(JavaFX 阶段)调用第二个 Activity (Android WebView) 并传递我要显示的 URL,当第二个 Activity 完成时,它会传回一个结果字符串。您可以通过执行 finish() 或使用后退按钮退出第二个 Activity。您需要在 AndroidManifest.xml 中为第二个 Activity 添加一个附加条目。
这是我的主要内容 activity:
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);
});
}
}
这是我的第二个 Activity(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 时崩溃 "can't create a handle without a Looper.prepare()"。句柄未明确创建,因此不清楚引用了哪个句柄。解决方案:创建一个循环器然后
实例化销毁它。您只能发出 Looper.prepare() 一次,否则它会崩溃。对 MainActivity.webView() 的后续调用在没有 Looper 的情况下工作。这是我的代码:
Looper.prepare();
new MainActivity();
Looper.myLooper().quitSafely();
我想通过
实现 java-to-java 脚本方法调用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");
或者有其他解决方法?
解决方案
我找到了从 JavaFXPorts 应用程序使用 Android 本机 WebView(而不是使用 JavaFX WebView)执行 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
}
});
}
};
}
我调用此 class 使用:
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 的 class 声明中复制了 "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 上的 运行 时,我得到了空值或 "not supported yet",尽管代码在桌面上运行正常。 (还没有在 IOS 上尝试过,但我也想在这里实现)。 我想检索网页标题并执行 javascript 从网页中检索其他数据。 因此,我的解决方案是在 Android 上 运行 时使用 Android 本机 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,但我正在努力关闭网页并return到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();
但我只是设法终止整个应用程序,而不是 return 到 JavaFX 阶段(即使我将它的值传递给此 class)。有什么想法吗?
PS:Looper.prepare();
是调用此代码所必需的,尽管我不明白我在这里做什么?
解决方案更新
我找到了从 JavaFXPorts 应用程序使用 Android 本机 WebView(而不是使用 JavaFX WebView)执行 JavaScript 的解决方案 - 请参阅下面的代码。 我通过使用 2 个活动解决了往返 JavaFX 阶段和 Android WebView(在我 2018 年 2 月 25 日的第一个答案中)的导航问题:我的主要 activity(JavaFX 阶段)调用第二个 Activity (Android WebView) 并传递我要显示的 URL,当第二个 Activity 完成时,它会传回一个结果字符串。您可以通过执行 finish() 或使用后退按钮退出第二个 Activity。您需要在 AndroidManifest.xml 中为第二个 Activity 添加一个附加条目。
这是我的主要内容 activity:
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);
});
}
}
这是我的第二个 Activity(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 时崩溃 "can't create a handle without a Looper.prepare()"。句柄未明确创建,因此不清楚引用了哪个句柄。解决方案:创建一个循环器然后 实例化销毁它。您只能发出 Looper.prepare() 一次,否则它会崩溃。对 MainActivity.webView() 的后续调用在没有 Looper 的情况下工作。这是我的代码:
Looper.prepare();
new MainActivity();
Looper.myLooper().quitSafely();