Android JavaScript-Funktionen in WebView aufrufen

249

Ich versuche, einige javascriptFunktionen aufzurufen , die auf einer htmlSeite in einem ausgeführt werden android webview. Ziemlich einfach, was der Code unten versucht - rufen Sie in der Android-App eine javascriptFunktion mit einer Testnachricht auf, die eine Java-Funktion in der Android-App zurückruft, die die Testnachricht per Toast anzeigt.

Die javascriptFunktion sieht aus wie:

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

In WebView habe ich javascriptohne Glück versucht, die folgenden Methoden aufzurufen :

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

Ich habe javascriptauf dem aktiviertWebView

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

Und hier ist die JavaKlasse

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();
    }
}

Ich habe viel Zeit damit verbracht, zu googeln, um zu sehen, was ich möglicherweise falsch mache. Alle Beispiele, die ich gefunden habe, verwenden diesen Ansatz. Hat hier jemand etwas falsch gesehen?

Bearbeiten: Es gibt mehrere andere externe javascriptDateien, auf die verwiesen und die in verwendet htmlwerden. Könnten sie das Problem sein?

user163757
quelle
13
Der Code, den Sie verwenden möchten, enthält keine Anführungszeichen auf der Javascript-Seite.
Paul de Vrieze
2
Beachten Sie, dass Sie seit Android 4.2 den @JavascriptInterfaceDecorator für die Java-Methoden verwenden müssen, die Sie über die JavaScript-Oberfläche für WebView verfügbar machen möchten.
Fred
1
Aus dem Code geht hervor myWebView.loadUrl("javascript:testEcho('Hello World!')");, dass Sie die HTML-Datei bereits an diese Webansicht angehängt haben. Können Sie mir sagen, wie Sie das gemacht haben?
Tony_craft

Antworten:

195

Ich habe herausgefunden, was das Problem war: fehlende Anführungszeichen im Parameter testEcho (). So bekam ich den Ruf zur Arbeit:

myWebView.loadUrl("javascript:testEcho('Hello World!')");
user163757
quelle
Bitte werfen Sie einen Blick auf diesen Link stackoverflow.com/questions/9079374/…
kamal_tech_view
Es ist schön, aber wie gibt man ein Java-Objekt als Argument an eine JavaScript-Funktion weiter?
Kovács Imre
1
@ KovácsImre String.Format ("Javascript: testEcho ('% d', '% d', '% d')", 1, 2, 3); Ich denke
user457015
Danke, ich habe es auch inzwischen herausgefunden.
Kovács Imre
1
Beginnend mit KitKat gibt es die EvaluateJavascript-
Heitor
117

Ab Kitkat verwenden Sie stattdessen die evaluJavascript-Methode loadUrl, um die folgenden Javascript-Funktionen aufzurufen

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        webView.evaluateJavascript("enable();", null);
    } else {
        webView.loadUrl("javascript:enable();");
    }
Prasad
quelle
2
Warum sollte evaluJavascript () anstelle von loadUrl () verwendet werden?
Kamran Bigdely
2
@kami loadUrl () wird die Seite neu laden und onPageFinished () erneut aufrufen
Zach
1
@Zach Es ist dann immer noch ein Problem bei Pre-KitKat, nicht wahr?
Vsevolod Ganin
2
@kami Wenn Sie Zach als Antwort hinzufügen, wird durch Auswerten von JavaScript () JavaScript asynchron ausgeführt. So können wir vermeiden, den UI-Thread mit evaluJavascript () zu blockieren.
Prasad
loadUrlAußerdem wurde die Softwaretastatur angezeigt, wenn ich versuche, den Wert in ein Eingabefeld einzugeben.
Joshua
34
public void run(final String scriptSrc) { 
        webView.post(new Runnable() {
            @Override
            public void run() { 
                webView.loadUrl("javascript:" + scriptSrc); 
            }
        }); 
    }
user340994
quelle
und wenn dies nicht funktioniert, mache einen neuen Thread (new Runnaable () {.. <wie oben> ..}). start ()
Radu Simionescu
26

Ich habe einen schönen Wrapper erstellt, um JavaScript-Methoden aufzurufen. Außerdem werden JavaScript-Fehler im Protokoll angezeigt:

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());
}

Sie müssen dies auch hinzufügen:

private class WebViewInterface{

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

Und fügen Sie diese Schnittstelle zu Ihrer Webansicht hinzu:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");
Ilya Gazman
quelle
Diese Funktion, auch wenn sie als Idee gut ist, hat eine falsche Implementierung. Dieser Aufruf: final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a);wird ergeben SyntaxErrormit Unexpected tokendenen ist nicht verwunderlich , wenn man sich die erzeugte js aussehen nennen:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
Lukasz ‚Severiaan‘ Grela
Selbst ein einfacher Aufruf callJavaScript(mBrowser, "alert", "abc", "def");erzeugt einen Fehler, da am Ende immer ein falsches Komma steht. -1 von mir.
Lukasz 'Severiaan' Grela
1
@ Lukasz'Severiaan'Grela tnx, das habe ich behoben. PS Das nächste Mal sei nicht faul, du könntest es selbst reparieren ...;)
Ilya Gazman
16
Zum späteren Nachschlagen: Bleiben wir positiv. Es ist nicht faul, auf einen Fehler im Code hinzuweisen. Es ist immer noch hilfreiches Feedback.
DW
Ich denke, das wird nicht funktionieren, wenn einer der Parameter 'Charakter' enthält. Beispiel:callJavascript(mBrowser, "can't do it");
Kostanos
18

Ja, Sie haben den Syntaxfehler. Wenn Sie Ihre Javascript-Fehler und Druckanweisungen in Ihrem Logcat erhalten möchten, müssen Sie die onConsoleMessage(ConsoleMessage cm)Methode in Ihrem WebChromeClient implementieren. Es gibt die vollständigen Stack-Traces wie die Webkonsole (Inspect-Element). Hier ist die Methode.

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

Nach der Implementierung erhalten Sie Ihre Javascript-Fehler und print-Anweisungen ( console.log) auf Ihrem Logcat.

Dinesh
quelle
2
Ich glaube, das ist auf dem WebChromeClient und nicht auf dem WebViewClient.
Michael Levy
Ja, du bist richtig @MichaelLevy. Danke, dass du mich auf diesen Fehler hingewiesen hast. Jetzt habe ich meine Antwort bearbeitet.
Dinesh
1
Danke, Ihre Antwort war genau das, was ich brauchte. Ich würde auch vorschlagen, dass Leute onJsAlert () im WebChromeClient überschreiben. Die Kombination aus Javascript-Protokollierung und Warnungen kann beim Debuggen von in der Webansicht gehostetem Javascript sehr hilfreich sein.
Michael Levy
13

Änderung der @ Ilya_Gazman-Antwort

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

erstellt JS-Aufrufe korrekt, z

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

Beachten Sie, dass Objekte nicht korrekt übergeben werden. Sie können sie jedoch serialisieren, bevor Sie sie als Argument übergeben.

Außerdem habe ich geändert, wohin der Fehler führt. Ich habe ihn in das Konsolenprotokoll umgeleitet, das abgehört werden kann von:

    webView.setWebChromeClient(new CustomWebChromeClient());

und 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
quelle
Vielen Dank. Entgeht diese Methode dem Zeichen innerhalb einer Variablen? Mit anderen Worten, wird es funktionieren mit : callJavascript(mBrowser, "can't do it");?
Kostanos
1
Habe
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')");
            }
        });
    }
}

Assets-Datei

<!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
quelle