web-dev-qa-db-fra.com

Comment obtenir une valeur de retour de javascript dans la vue Web d'Android?

Je souhaite obtenir une valeur de retour de Javascript dans Android. Je peux le faire avec iPhone, mais pas avec Android. J'ai utilisé loadUrl, mais il est retourné vide au lieu d'un objet. Quelqu'un peut-il m'aider? Merci.

68
Cuong Ta

Identique à Keith mais réponse plus courte

webView.addJavascriptInterface(this, "Android");
webView.loadUrl("javascript:Android.onData(functionThatReturnsSomething)");

Et implémenter la fonction

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

N'oubliez pas de supprimer la liste onData de proguard (si vous avez activé proguard)

62
Kirill Kulakov

Voici comment vous pouvez y arriver:

Ajoutez ce client à votre 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;
        }
    }

Maintenant, dans votre appel javascript, faites:

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

Maintenant, dans l'appel onJsAlert, "message" contiendra la valeur renvoyée.

61
Felix Khazin

Utilisez addJavascriptInterface() pour ajouter un objet Java à l'environnement Javascript. Demandez à votre JavaScript d'appeler une méthode sur cet objet Java pour fournir sa "valeur de retour".

17
CommonsWare

Voici ce que je suis venu avec aujourd'hui. Il est thread-safe, raisonnablement efficace et permet l'exécution javascript synchrone à partir de Java pour un WebView Android .

Fonctionne dans Android 2.2 et plus. (Requiert commons-lang car j'ai besoin que mes extraits de code soient transmis à eval () sous forme de chaîne Javascript. Vous pouvez supprimer cette dépendance en encapsulant le code non pas entre guillemets, mais avec function(){})

Tout d'abord, ajoutez ceci à votre fichier 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);
}

Ajoutez ensuite ceci à votre activité 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);
    }
}
10
Keith

La solution suggérée par @Felix Khazin fonctionne, mais il manque un point clé.

L'appel javascript doit être effectué après le chargement de la page Web dans la WebView. Ajoutez cette WebViewClient à la WebView, avec la WebChromeClient.

Exemple complet:

@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;
    }
}
5
Mitko Katsev

Sur API 19+, la meilleure façon de faire est d’appeler assessmentJavascript sur votre WebView:

webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() {
    @Override public void onReceiveValue(String value) {
        // value is the result returned by the Javascript as JSON
    }
});

Réponse associée avec plus de détail: https://stackoverflow.com/a/20377857

3
David

En tant que variante alternative utilisant un schéma personnalisé:

Activité principale

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 (situé dans les actifs pliés)

<!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>
0
yoAlex5