Android Wywoływanie funkcji JavaScript w WebView


249

Próbuję wywołać niektóre javascriptfunkcje znajdujące się na htmlstronie działającej w pliku android webview. Całkiem proste, co kod próbuje zrobić poniżej - z aplikacji na Androida wywołaj javascriptfunkcję z komunikatem testowym, który wywołuje z powrotem funkcję java w aplikacji na Androida, która wyświetla wiadomość testową za pośrednictwem toast.

Te javascriptspojrzenia działać jak:

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

Z WebView próbowałem wywołać javascriptnastępujące sposoby bez powodzenia:

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

I nie pozwalają javascriptnaWebView

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

I oto Javaklasa

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

Spędziłem dużo czasu, szukając w Google, co robię źle. Wszystkie znalezione przykłady wykorzystują to podejście. Czy ktoś widzi tutaj coś nie tak?

Edycja: istnieje kilka innych javascriptplików zewnętrznych , do których się odwołuje i które są używane w html, czy to może być problem?


13
Kod, którego próbujesz użyć, pomija cytaty po stronie javascript.
— Paul de Vrieze

2
Pamiętaj, że od Androida 4.2 musisz używać @JavascriptInterfacedekoratora w metodach Java, które chcesz udostępnić WebView poprzez interfejs JavaScript.
— Fred

1
Z kodu myWebView.loadUrl("javascript:testEcho('Hello World!')");rozumiem, że już załączyłeś plik HTML do tego widoku. Czy możesz mi powiedzieć, jak to zrobiłeś?
— Tony_craft

Odpowiedzi:


195

Zrozumiałem, na czym polega problem: brakujące cudzysłowy w parametrze testEcho (). Oto jak dostałem wezwanie do pracy:

myWebView.loadUrl("javascript:testEcho('Hello World!')");


To miłe, ale jak przekazać obiekt Java do funkcji JavaScript jako argument?
— Kovács Imre

1
@ KovácsImre String.Format ("javascript: testEcho ('% d', '% d', '% d') ', 1, 2, 3); Chyba
— użytkownik457015

Dzięki, dowiedziałem się również w międzyczasie.
— Kovács Imre

1
Zaczynając od KitKat, istnieje metoda oceny Javascript, sprawdź stackoverflow.com/a/32163655/3063226
— Heitor,

117

Począwszy od KitKata, użyj metody replaceJavascript zamiast loadUrl, aby wywołać funkcje javascript, jak poniżej

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        webView.evaluateJavascript("enable();", null);
    } else {
        webView.loadUrl("javascript:enable();");
    }

2
dlaczego warto korzystać z ocenyJavascript () zamiast loadUrl ()?
— Kamran Bigdely

2
@kami loadUrl () ponownie załaduje stronę i ponownie wywoła funkcję onPageFinished ()
— Zach.

1
@Zach Nadal jest to problem w wersji wstępnej KitKat, prawda?
— Vsevolod Ganin

2
@kami Dodając do odpowiedzi Zach, EvaluJavascript (), uruchomi JavaScript asynchronicznie. Dzięki temu możemy uniknąć blokowania wątku interfejsu użytkownika za pomocą EvaluJavascript ().
— Prasad

loadUrlspowodowało także, że klawiatura oprogramowania wyświetla się, gdy próbuję wpisać wartość w polu wejściowym.
— Joshua

34
public void run(final String scriptSrc) { 
        webView.post(new Runnable() {
            @Override
            public void run() { 
                webView.loadUrl("javascript:" + scriptSrc); 
            }
        }); 
    }

a jeśli to nie zadziała, zrób nowy Wątek (new Runnaable () {.. <jak wyżej> ..}). start ()
— Radu Simionescu,

26

Stworzyłem ładne opakowanie do wywoływania metod JavaScript; pokazuje także błędy JavaScript w logu:

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

Musisz to również dodać:

private class WebViewInterface{

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

I dodaj ten interfejs do swojego widoku internetowego:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");

Ta funkcja, jakkolwiek dobra jako pomysł, ma niewłaściwe wdrożenie. To wezwanie: final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a);podda się SyntaxErrorz Unexpected tokenco nie jest zaskakujące, kiedy obejrzysz generowanych js zadzwonić:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
— Lukasz Severiaan 'Grela

Nawet proste wywołanie callJavaScript(mBrowser, "alert", "abc", "def");generuje błąd, ponieważ na końcu zawsze występuje nieuczciwy przecinek. -1 ode mnie.
— Łukasz „Severiaan” Grela

1
@ Lukasz'Severiaan'Grela tnx, naprawiłem to. PS Następnym razem nie bądź leniwy, możesz sam to naprawić ...;)
— Ilya Gazman

16
Do wykorzystania w przyszłości: bądźmy pozytywni. Wskazanie błędu w kodzie nie jest leniwe. To wciąż pomocne informacje zwrotne.
— DW

Myślę, że to nie zadziała, jeśli którykolwiek z parametrów ma znak „?” Przykład:callJavascript(mBrowser, "can't do it");
— Kostanos

18

Tak, masz błąd składniowy. Jeśli chcesz uzyskać błędy JavaScript i instrukcje drukowania w logcat, musisz zaimplementować tę onConsoleMessage(ConsoleMessage cm)metodę w swoim WebChromeClient. Daje kompletne ślady stosu, takie jak konsola internetowa (element Inspect). Oto metoda.

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

Po wdrożeniu otrzymasz swoje błędy JavaScript i wypisz instrukcje ( console.log) na swoim logcat.


2
Uważam, że jest on w WebChromeClient, a nie WebViewClient.
— Michael Levy

Tak, masz rację @MichaelLevy. Dzięki za wskazanie mi tego błędu. Teraz zredagowałem swoją odpowiedź.
— Dinesh

1
Dzięki, twoja odpowiedź była właśnie tym, czego potrzebowałem. Sugerowałbym również, aby ludzie zastąpili metodę onJsAlert () w WebChromeClient. Kombinacja rejestrowania javascript i alertów może być naprawdę pomocna podczas debugowania kodu JavaScript hostowanego w widoku sieciowym.
— Michael Levy,

13

Modyfikacja odpowiedzi @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);
    }

poprawnie utworzy połączenia JS np

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

Zauważ, że obiekty nie zostaną poprawnie przekazane - ale możesz je serializować przed przekazaniem jako argument.

Zmieniłem także, gdzie pojawia się błąd, przekierowałem go do dziennika konsoli, który można odsłuchać:

    webView.setWebChromeClient(new CustomWebChromeClient());

i klient

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

Dzięki. Czy ta metoda uniknie znaku wewnątrz dowolnej zmiennej? Innymi słowy, czy będzie działać z callJavascript(mBrowser, "can't do it");:?
— Kostanos

1
Właśnie zredagowałem odpowiedź i dodałem ucieczkę do postaci
— Kostanos

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

plik aktywów

<!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>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.