Android Appeler les fonctions JavaScript dans WebView

249

J'essaie d'appeler certaines javascriptfonctions assis sur une htmlpage en cours d'exécution dans un android webview. Assez simple ce que le code essaie de faire ci-dessous - à partir de l'application Android, appelez une javascriptfonction avec un message de test, qui appelle à son tour une fonction java dans l'application Android qui affiche le message de test via toast.

La javascriptfonction ressemble à:

function testEcho(message){
     window.JSInterface.doEchoTest(message);
}

Depuis WebView, j'ai essayé d'appeler les javascriptméthodes suivantes sans succès:

myWebView.loadUrl("javascript:testEcho(Hello World!)");
mWebView.loadUrl("javascript:(function () { " + "testEcho(Hello World!);" + "})()");

J'ai activé javascriptleWebView

myWebView.getSettings().setJavaScriptEnabled(true);
// register class containing methods to be exposed to JavaScript
myWebView.addJavascriptInterface(myJSInterface, "JSInterface"); 

Et voici la Javaclasse

public class JSInterface{

private WebView mAppView;
public JSInterface  (WebView appView) {
        this.mAppView = appView;
    }

    public void doEchoTest(String echo){
        Toast toast = Toast.makeText(mAppView.getContext(), echo, Toast.LENGTH_SHORT);
        toast.show();
    }
}

J'ai passé beaucoup de temps à faire des recherches pour voir ce que je fais mal. Tous les exemples que j'ai trouvés utilisent cette approche. Quelqu'un voit-il quelque chose de mal ici?

Edit: Il existe plusieurs autres javascriptfichiers externes référencés et utilisés dans le html, pourraient-ils être le problème?

user163757
la source
13
Le code que vous essayez d'utiliser manque des guillemets du côté javascript.
Paul de Vrieze
2
Veuillez noter que depuis Android 4.2, vous devez utiliser le @JavascriptInterfacedécorateur sur les méthodes Java que vous souhaitez mettre à la disposition de WebView via l'interface JavaScript.
Fred
1
D'après le code, myWebView.loadUrl("javascript:testEcho('Hello World!')");je comprends que vous avez déjà joint le fichier html à cette vue Web. Pourriez-vous me dire comment vous avez fait ça?
Tony_craft

Réponses:

195

J'ai compris quel était le problème: des guillemets manquants dans le paramètre testEcho (). C'est ainsi que j'ai reçu l'appel au travail:

myWebView.loadUrl("javascript:testEcho('Hello World!')");
user163757
la source
veuillez consulter ces liens stackoverflow.com/questions/9079374/…
kamal_tech_view
C'est bien, mais comment passer un objet Java à une fonction JavaScript comme argument?
Kovács Imre
1
@ KovácsImre String.Format ("javascript: testEcho ('% d', '% d', '% d')", 1, 2, 3); Je suppose
user457015
Merci, je l'ai aussi découvert entre-temps.
Kovács Imre
1
À partir de KitKat, il y a la méthode assessJavascript
Heitor
117

À partir de kitkat, utilisez la méthode assessJavascript à la place de loadUrl pour appeler les fonctions javascript comme ci-dessous

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        webView.evaluateJavascript("enable();", null);
    } else {
        webView.loadUrl("javascript:enable();");
    }
Prasad
la source
2
pourquoi utiliser assessJavascript () au lieu de loadUrl ()?
Kamran Bigdely
2
@kami loadUrl () rechargera la page et appellera à nouveau onPageFinished ()
Zach
1
@Zach C'est toujours un problème sur pre-KitKat, n'est-ce pas?
Vsevolod Ganin
2
@kami En ajoutant à la réponse Zach, assessJavascript (), exécutera JavaScript de manière asynchrone. Ainsi, nous pouvons éviter de bloquer le thread d'interface utilisateur en utilisant assessJavascript ().
Prasad
loadUrla également provoqué l'affichage du clavier logiciel lorsque j'essaie de remplir la valeur dans un champ de saisie.
Joshua
34
public void run(final String scriptSrc) { 
        webView.post(new Runnable() {
            @Override
            public void run() { 
                webView.loadUrl("javascript:" + scriptSrc); 
            }
        }); 
    }
user340994
la source
et si cela ne fonctionne pas, faites un nouveau Thread (nouveau Runnaable () {.. <comme ci-dessus> ..}). start ()
Radu Simionescu
26

J'ai créé un joli wrapper pour appeler les méthodes JavaScript; il montre également les erreurs JavaScript dans le journal:

private void callJavaScript(String methodName, Object...params){
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("javascript:try{");
    stringBuilder.append(methodName);
    stringBuilder.append("(");
    for (int i = 0; i < params.length; i++) {
        Object param = params[i];
        if(param instanceof String){
            stringBuilder.append("'");
            stringBuilder.append(param.toString().replace("'", "\\'"));
            stringBuilder.append("'");
        }
        if(i < params.length - 1){
            stringBuilder.append(",");
        }
    }
    stringBuilder.append(")}catch(error){Android.onError(error.message);}");
    webView.loadUrl(stringBuilder.toString());
}

Vous devez également ajouter ceci:

private class WebViewInterface{

    @JavascriptInterface
    public void onError(String error){
        throw new Error(error);
    }
}

Et ajoutez cette interface à votre vue Web:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");
Ilya Gazman
la source
Cette fonction, aussi bonne soit-elle, a une implémentation incorrecte. Cet appel: final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a);va céder SyntaxErroravec Unexpected tokenqui ne surprend pas quand vous regardez les js générés appeler:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
Lukasz « Severiaan » Grela
Même un simple appel callJavaScript(mBrowser, "alert", "abc", "def");génère une erreur car il y a toujours une virgule escroc à la fin. -1 de moi.
Lukasz 'Severiaan' Grela
1
@ Lukasz'Severiaan'Grela tnx, j'ai corrigé cela. PS La prochaine fois, ne soyez pas paresseux, vous pouvez le réparer vous-même ...;)
Ilya Gazman
16
Pour référence future: Restons positifs. Souligner une erreur dans le code n'est pas paresseux. Ce sont toujours des commentaires utiles.
DW du
Je suppose que cela ne fonctionnera pas si l'un des paramètres contient du caractère? Exemple:callJavascript(mBrowser, "can't do it");
Kostanos
18

Oui, vous avez l'erreur de syntaxe. Si vous souhaitez obtenir vos erreurs Javascript et imprimer des instructions dans votre logcat, vous devez implémenter la onConsoleMessage(ConsoleMessage cm)méthode dans votre WebChromeClient. Il donne les traces de pile complètes comme la console Web (élément Inspect). Voici la méthode.

public boolean onConsoleMessage(ConsoleMessage cm) 
    {
        Log.d("Message", cm.message() + " -- From line "
                             + cm.lineNumber() + " of "
                             + cm.sourceId() );
        return true;
    }

Après la mise en œuvre, vous obtiendrez vos erreurs Javascript et imprimerez les instructions ( console.log) sur votre logcat.

Dinesh
la source
2
Je crois que c'est sur le WebChromeClient et non sur le WebViewClient.
Michael Levy
Oui, vous avez raison @MichaelLevy. Merci de m'avoir signalé cette erreur. Maintenant, j'ai modifié ma réponse.
Dinesh
1
Merci, votre réponse était exactement ce dont j'avais besoin. Je suggère également aux utilisateurs de remplacer onJsAlert () dans WebChromeClient. La combinaison de la journalisation javascript et des alertes peut être très utile lors du débogage de Javascript hébergé dans la vue Web.
Michael Levy
13

Modification de la réponse @Ilya_Gazman

    private void callJavaScript(WebView view, String methodName, Object...params){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("javascript:try{");
        stringBuilder.append(methodName);
        stringBuilder.append("(");
        String separator = "";
        for (Object param : params) {               
            stringBuilder.append(separator);
            separator = ",";
            if(param instanceof String){
                stringBuilder.append("'");
            }
                stringBuilder.append(param.toString().replace("'", "\\'"));
            if(param instanceof String){
                stringBuilder.append("'");
            }

        }
        stringBuilder.append(")}catch(error){console.error(error.message);}");
        final String call = stringBuilder.toString();
        Log.i(TAG, "callJavaScript: call="+call);


        view.loadUrl(call);
    }

créera correctement les appels JS, par exemple

callJavaScript(mBrowser, "alert", "abc", "def");
//javascript:try{alert('abc','def')}catch(error){console.error(error.message);}
callJavaScript(mBrowser, "alert", 1, true, "abc");
//javascript:try{alert(1,true,'abc')}catch(error){console.error(error.message);}

Notez que les objets ne seront pas transmis correctement - mais vous pouvez les sérialiser avant de les passer en argument.

J'ai également changé l'emplacement de l'erreur, je l'ai détourné vers le journal de la console qui peut être écouté par:

    webView.setWebChromeClient(new CustomWebChromeClient());

et client

class CustomWebChromeClient extends WebChromeClient {
    private static final String TAG = "CustomWebChromeClient";

    @Override
    public boolean onConsoleMessage(ConsoleMessage cm) {
        Log.d(TAG, String.format("%s @ %d: %s", cm.message(),
                cm.lineNumber(), cm.sourceId()));
        return true;
    }
}
Lukasz 'Severiaan' Grela
la source
Merci. Cette méthode échappera-t-elle au caractère à l'intérieur d'une variable? En d'autres termes, cela fonctionnera-t-il avec callJavascript(mBrowser, "can't do it");:?
Kostanos
1
Je
viens de modifier
2

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

MainActivity.java


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.bluapp.androidview.R;

public class WebViewActivity3 extends AppCompatActivity {
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view3);
        webView = (WebView) findViewById(R.id.webView);
        webView.setWebViewClient(new WebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl("file:///android_asset/webview1.html");


        webView.setWebViewClient(new WebViewClient(){
            public void onPageFinished(WebView view, String weburl){
                webView.loadUrl("javascript:testEcho('Javascript function in webview')");
            }
        });
    }
}

fichier d'actifs

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>WebView1</title>
<meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
</head>

<body style="background-color:#212121">
<script type="text/javascript">
function testEcho(p1){
document.write(p1);
}
</script>
</body>

</html>
dariush
la source