如何在Android的WebView中从javascript获取返回值?

如何在Android的WebView中从javascript获取返回值?,android,return,Android,Return,我想从Android中的Javascript获取一个返回值。我可以用iPhone来做,但用Android不行。我使用了loadUrl,但它返回的是void而不是对象。有人能帮我吗?使用addJavascriptInterface()向Javascript环境添加Java对象。让您的Javascript在该Java对象上调用一个方法以提供其“返回值”。下面是一个如何实现它的技巧: 将此客户端添加到您的WebView: final class MyWebChromeClient extends We

我想从Android中的Javascript获取一个返回值。我可以用iPhone来做,但用Android不行。我使用了loadUrl,但它返回的是void而不是对象。有人能帮我吗?

使用
addJavascriptInterface()
向Javascript环境添加Java对象。让您的Javascript在该Java对象上调用一个方法以提供其“返回值”。

下面是一个如何实现它的技巧:

将此客户端添加到您的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;
        }
    }
webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() {
    @Override public void onReceiveValue(String value) {
        // value is the result returned by the Javascript as JSON
    }
});
现在在javascript调用中执行以下操作:

webView.loadUrl("javascript:alert(functionThatReturnsSomething)");

现在,在onJsAlert调用中,“message”将包含返回值。

以下是我今天想到的。它是线程安全的,效率相当高,并且允许在Android WebView中从Java执行同步Javascript

适用于Android 2.2及更高版本。(需要commons lang,因为我需要将代码片段作为Javascript字符串传递给eval()。您可以通过将代码不用引号括起来,而是用
function(){}
来删除此依赖项)

首先,将其添加到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);
}
然后,将其添加到您的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);
    }
}
private Handler=new Handler();
私有最终AtomicInteger evalJsIndex=新的AtomicInteger(0);
private final Map jsReturnValues=new HashMap();
私有最终对象jsReturnValueLock=新对象();
私有网络视图;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
webView=(webView)findviewbyd(R.id.webView);
addJavascriptInterface(新的MyJavascriptInterface(this),“androidInterface”);
}
公共字符串evalJs(最终字符串js){
final int index=evalJsIndex.incrementAndGet();
handler.post(新的Runnable(){
公开募捐{
loadUrl(“javascript:evalJsForAndroid(“+index+”,”+
“\”“+StringEscapeUtils.escapeEcmaScript(js)+”\”);
}
});
返回waitForJsReturnValue(索引,10000);
}
私有字符串waitForJsReturnValue(int索引,int waitMs){
长启动=System.currentTimeMillis();
while(true){
长时间运行=System.currentTimeMillis()-开始;
如果(经过>等待毫秒)
打破
已同步(jsReturnValueLock){
String value=jsReturnValues.remove(索引);
if(值!=null)
返回值;
long-toWait=waitMs-(System.currentTimeMillis()-start);
如果(等待>0)
试一试{
jsReturnValueLock.wait(等待);
}捕捉(中断异常e){
打破
}
其他的
打破
}
}
Log.e(“MyActivity”、“放弃;等待”+(waitMs/1000)+“返回值秒”+索引);
返回“”;
}
私有void processJsReturnValue(int索引,字符串值){
已同步(jsReturnValueLock){
jsReturnValues.put(索引,值);
jsReturnValueLock.notifyAll();
}
}
私有静态类MyJavascriptInterface{
私人活动;
公共MyJavascriptInterface(MyActivity活动){
这个。活动=活动;
}
//此注释在Jelly Bean及更高版本中是必需的:
@JavascriptInterface
public void processReturnValue(int索引,字符串值){
activity.processJsReturnValue(索引、值);
}
}

Keith
相同,但答案较短

webView.addJavascriptInterface(this, "android");
webView.loadUrl("javascript:android.onData(functionThatReturnsSomething)");
并实现了该功能

@JavascriptInterface
public void onData(String value) {
   //.. do something with the data
}

不要忘记从
proguard
列表中删除
onData
(如果您启用了proguard)

@Felix Khazin建议的解决方案是可行的,但缺少一个关键点

javascript调用应该在加载
WebView
中的网页后进行。将此
WebViewClient
WebChromeClient
一起添加到
WebView

完整示例:

@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;
    }
}

在API 19+上,最好的方法是调用您的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;
        }
    }
webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() {
    @Override public void onReceiveValue(String value) {
        // value is the result returned by the Javascript as JSON
    }
});
webView.evaluateJavascript(“foo.bar()”,new ValueCallback()){
@重写公共void onReceiveValue(字符串值){
//值是Javascript作为JSON返回的结果
}
});

更详细的相关回答:

作为另一种变体,使用自定义方案
Android原生代码HTML/JS代码
进行通信。例如,MRAID使用这种技术

main活动

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(位于折叠的资产中)


JavaScript视图
函数showtoots(){
window.location=“customschema://goto/";
}

您可以这样做:

[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);
    }
}
[活动(标签=“@string/app_name”,Theme=“@style/AppTheme”,MainLauncher=true)]
公共类MainActivity:AppCompativeActivity
{
公共网络视图;
公共静态文本视图文本视图;
创建时受保护的覆盖无效(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(这个,savedInstanceState);
AddFlags(WindowManagerFlags.Fullscreen);
Window.ClearFlags(WindowManagerFlags.ForceNotFullscreen);
//从“主”布局资源设置视图
SetContentView(Resource.Layout.main);
web_view=findviewbyd(Resource.Id.webView);
textView=FindViewById(Resource.Id.textView);
web_view.Settings.JavaScriptEnabled=true;
SetWebViewClient(新的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(请求代码、权限、GrantResult);
base.onRequestPermissions