如何在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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 09:34:42  来源:igfitidea点击:

How to get return value from javascript in WebView of Android?

androidreturn

提问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 Keithbut 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 onDatafrom proguardlist (if you have enabled proguard)

不要忘记onDataproguard列表中删除(如果您启用了 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 WebViewis loaded. Add this WebViewClientto the WebView, along with the WebChromeClient.

javascript 调用应在WebView加载 中的网页后进行。这个添加WebViewClientWebView,与一起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);
    }
}