如何在Android的WebView中从javascript获取返回值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3298597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get return value from javascript in WebView of Android?
提问by Cuong Ta
I want to get a return value from Javascript in Android. I can do it with the iPhone, but I can't with Android. I used loadUrl, but it returned void instead of an object. Can anybody help me?
我想从 Android 中的 Javascript 获取返回值。我可以用 iPhone 做到,但我不能用 Android。我使用了 loadUrl,但它返回的是 void 而不是对象。有谁能够帮助我?
回答by Kirill Kulakov
Same as Keith
but shorter answer
相同Keith
但更短的答案
webView.addJavascriptInterface(this, "android");
webView.loadUrl("javascript:android.onData(functionThatReturnsSomething)");
And implement the function
并实现功能
@JavascriptInterface
public void onData(String value) {
//.. do something with the data
}
Don't forget to remove the onData
from proguard
list (if you have enabled proguard)
不要忘记onData
从proguard
列表中删除(如果您启用了 proguard)
回答by Felix Khazin
Here's a hack on how you can accomplish it:
这是一个关于如何完成它的黑客:
Add this Client to your WebView:
将此客户端添加到您的 WebView:
final class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
Now in your javascript call do:
现在在您的 javascript 调用中执行以下操作:
webView.loadUrl("javascript:alert(functionThatReturnsSomething)");
Now in the onJsAlert call "message" will contain the returned value.
现在在 onJsAlert 调用中“message”将包含返回值。
回答by CommonsWare
Use addJavascriptInterface()
to add a Java object to the Javascript environment. Have your Javascript call a method on that Java object to supply its "return value".
使用addJavascriptInterface()
Java对象添加到JavaScript环境。让您的 Javascript 调用该 Java 对象上的方法以提供其“返回值”。
回答by Keith
Here's what I came up with today. It's thread-safe, reasonably efficient, and allows for synchronous Javascript execution from Java for an Android WebView.
这就是我今天想到的。它是线程安全的、相当高效的,并且允许从 Java 为 Android WebView 同步执行 Javascript。
Works in Android 2.2 and up. (Requires commons-lang because I need my code snippets passed to eval() as a Javascript string. You could remove this dependency by wrapping the code not in quotation marks, but in function(){}
)
适用于 Android 2.2 及更高版本。(需要 commons-lang 因为我需要将我的代码片段作为 Javascript 字符串传递给 eval()。您可以通过将代码包装在引号中而不是在 中来删除此依赖项function(){}
)
First, add this to your Javascript file:
首先,将其添加到您的 Javascript 文件中:
function evalJsForAndroid(evalJs_index, jsString) {
var evalJs_result = "";
try {
evalJs_result = ""+eval(jsString);
} catch (e) {
console.log(e);
}
androidInterface.processReturnValue(evalJs_index, evalJs_result);
}
Then, add this to your Android activity:
然后,将其添加到您的 Android 活动中:
private Handler handler = new Handler();
private final AtomicInteger evalJsIndex = new AtomicInteger(0);
private final Map<Integer, String> jsReturnValues = new HashMap<Integer, String>();
private final Object jsReturnValueLock = new Object();
private WebView webView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = (WebView) findViewById(R.id.webView);
webView.addJavascriptInterface(new MyJavascriptInterface(this), "androidInterface");
}
public String evalJs(final String js) {
final int index = evalJsIndex.incrementAndGet();
handler.post(new Runnable() {
public void run() {
webView.loadUrl("javascript:evalJsForAndroid(" + index + ", " +
"\"" + StringEscapeUtils.escapeEcmaScript(js) + "\")");
}
});
return waitForJsReturnValue(index, 10000);
}
private String waitForJsReturnValue(int index, int waitMs) {
long start = System.currentTimeMillis();
while (true) {
long elapsed = System.currentTimeMillis() - start;
if (elapsed > waitMs)
break;
synchronized (jsReturnValueLock) {
String value = jsReturnValues.remove(index);
if (value != null)
return value;
long toWait = waitMs - (System.currentTimeMillis() - start);
if (toWait > 0)
try {
jsReturnValueLock.wait(toWait);
} catch (InterruptedException e) {
break;
}
else
break;
}
}
Log.e("MyActivity", "Giving up; waited " + (waitMs/1000) + "sec for return value " + index);
return "";
}
private void processJsReturnValue(int index, String value) {
synchronized (jsReturnValueLock) {
jsReturnValues.put(index, value);
jsReturnValueLock.notifyAll();
}
}
private static class MyJavascriptInterface {
private MyActivity activity;
public MyJavascriptInterface(MyActivity activity) {
this.activity = activity;
}
// this annotation is required in Jelly Bean and later:
@JavascriptInterface
public void processReturnValue(int index, String value) {
activity.processJsReturnValue(index, value);
}
}
回答by David
On API 19+, the best way to do this is to call evaluateJavascripton your WebView:
在 API 19+ 上,最好的方法是在您的 WebView 上调用evaluateJavascript:
webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() {
@Override public void onReceiveValue(String value) {
// value is the result returned by the Javascript as JSON
}
});
Related answer with more detail: https://stackoverflow.com/a/20377857
更详细的相关答案:https: //stackoverflow.com/a/20377857
回答by Mitko Katsev
The solution that @Felix Khazin suggested works, but there is one key point missing.
@Felix Khazin 建议的解决方案有效,但缺少一个关键点。
The javascript call should be made after the web page in the WebView
is loaded. Add this WebViewClient
to the WebView
, along with the WebChromeClient
.
javascript 调用应在WebView
加载 中的网页后进行。这个添加WebViewClient
的WebView
,与一起WebChromeClient
。
Full Example:
完整示例:
@Override
public void onCreate(Bundle savedInstanceState) {
...
WebView webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new MyWebViewClient());
webView.setWebChromeClient(new MyWebChromeClient());
webView.loadUrl("http://example.com");
}
private class MyWebViewClient extends WebViewClient {
@Override
public void onPageFinished (WebView view, String url){
view.loadUrl("javascript:alert(functionThatReturnsSomething())");
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
private class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
回答by yoAlex5
As an alternative variant that uses a custom scheme:
作为使用自定义方案的替代变体:
MainActivity
主要活动
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
final WebView webview = new CustomWebView(this);
setContentView(webview);
webview.loadUrl("file:///android_asset/customPage.html");
webview.postDelayed(new Runnable() {
@Override
public void run() {
//Android -> JS
webview.loadUrl("javascript:showToast()");
}
}, 1000);
}
}
CustomWebView
自定义网页视图
public class CustomWebView extends WebView {
public CustomWebView(Context context) {
super(context);
setup();
}
@SuppressLint("SetJavaScriptEnabled")
private void setup() {
setWebViewClient(new AdWebViewClient());
getSettings().setJavaScriptEnabled(true);
}
private class AdWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("customschema://")) {
//parse uri
Toast.makeText(CustomWebView.this.getContext(), "event was received", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
}
}
customPage.html(located in the assets folded)
customPage.html(位于折叠的资产中)
<!DOCTYPE html>
<html>
<head>
<title>JavaScript View</title>
<script type="text/javascript">
<!--JS -> Android-->
function showToast() {
window.location = "customschema://goto/";
}
</script>
</head>
<body>
</body>
</html>
回答by Sherif Riad
You can do it like this:
你可以这样做:
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public WebView web_view;
public static TextView textView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Window.AddFlags(WindowManagerFlags.Fullscreen);
Window.ClearFlags(WindowManagerFlags.ForceNotFullscreen);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.main);
web_view = FindViewById<WebView>(Resource.Id.webView);
textView = FindViewById<TextView>(Resource.Id.textView);
web_view.Settings.JavaScriptEnabled = true;
web_view.SetWebViewClient(new SMOSWebViewClient());
web_view.LoadUrl("https://stns.egyptair.com");
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public class SMOSWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return false;
}
public override void OnPageFinished(WebView view, string url)
{
view.EvaluateJavascript("document.getElementsByClassName('notf')[0].innerHTML;", new JavascriptResult());
}
}
public class JavascriptResult : Java.Lang.Object, IValueCallback
{
public string Result;
public void OnReceiveValue(Java.Lang.Object result)
{
string json = ((Java.Lang.String)result).ToString();
Result = json;
MainActivity.textView.Text = Result.Replace("\"", string.Empty);
}
}