Android Chiamata delle funzioni JavaScript in WebView


249

Sto provando a chiamare alcune javascriptfunzioni che si trovano in una htmlpagina all'interno di un android webview. Piuttosto semplice ciò che il codice tenta di fare di seguito: dall'app Android, chiama una javascriptfunzione con un messaggio di prova, che a sua volta richiama una funzione Java nell'app Android che visualizza il messaggio di prova tramite toast.

La javascriptfunzione è simile a:

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

Da WebView, ho provato a chiamare javascripti seguenti modi senza fortuna:

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

Ho abilitato javascriptsulWebView

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

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

Ho passato molto tempo a cercare su Google cosa potrei fare di sbagliato. Tutti gli esempi che ho trovato usano questo approccio. Qualcuno vede qualcosa di sbagliato qui?

Modifica: ci sono molti altri javascriptfile esterni a cui viene fatto riferimento e utilizzati in html, potrebbero essere il problema?


13
Il codice che si tenta di utilizzare manca citazioni sul lato javascript.
Paul de Vrieze,

2
Si noti che a partire da Android 4.2 è necessario utilizzare il @JavascriptInterfacedecoratore sui metodi Java che si desidera rendere disponibili per WebView tramite l'interfaccia JavaScript.
Fred,

1
Dal codice myWebView.loadUrl("javascript:testEcho('Hello World!')");ho capito che hai già allegato il file html a quella vista web. Potresti dirmi come hai fatto?
Tony_craft,

Risposte:


195

Ho capito qual era il problema: virgolette mancanti nel parametro testEcho (). Ecco come ho fatto funzionare la chiamata:

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

si prega di dare un'occhiata a questi link in stackoverflow.com/questions/9079374/...
kamal_tech_view

È carino, ma come si fa a passare un oggetto Java a una funzione JavaScript come argomento?
Kovács Imre,

1
@ KovácsImre String.Format ("javascript: testEcho ('% d', '% d', '% d')", 1, 2, 3); Immagino
user457015 il

Grazie, l'ho scoperto anche nel frattempo.
Kovács Imre,

1
A partire da KitKat c'è il metodo assessJavascript
Heitor

117

Da kitkat in poi usa il metodo assessJavascript invece loadUrl per chiamare le funzioni javascript come sotto

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

2
perché usare assessJavascript () invece di loadUrl ()?
Kamran Bigdely,

2
@kami loadUrl () ricaricherà la pagina e chiamerà di nuovo onPageFinished ()
Zach

1
@Zach Quindi è ancora un problema su pre-KitKat, vero?
Vsevolod Ganin,

2
@kami Aggiungendo alla risposta Zach, assessJavascript (), eseguirà JavaScript in modo asincrono. Quindi possiamo evitare di bloccare il thread dell'interfaccia utente usando assessJavascript ().
Prasad,

loadUrlha anche causato la visualizzazione della tastiera del software quando provo a compilare il valore in un campo di input.
Giosuè,

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

e se questo non funziona, fai nuovo thread (nuovo Runnaable () {.. <come sopra> ..}). start ()
Radu Simionescu

26

Ho creato un simpatico wrapper per chiamare i metodi JavaScript; mostra anche errori JavaScript nel registro:

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

Devi aggiungere anche questo:

private class WebViewInterface{

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

E aggiungi questa interfaccia alla tua visualizzazione Web:

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

Questa funzione, per quanto buona come idea, ha un'implementazione sbagliata. Questa chiamata: final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a);produrrà a SyntaxErrorcon Unexpected tokenche non è sorprendente quando si sarà guardare ai js generati chiamare:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
Lukasz 'Severiaan' Grela

Anche la semplice chiamata callJavaScript(mBrowser, "alert", "abc", "def");genera errore poiché alla fine c'è sempre una virgola non valida. -1 da me.
Lukasz "Severiaan" Grela,

1
@ Lukasz'Severiaan'Grela tnx, l'ho risolto. PS La prossima volta non essere pigro potresti risolverlo da solo ...;)
Ilya Gazman,

16
Per riferimento futuro: restiamo positivi. Sottolineare un errore nel codice non è pigro. È ancora un feedback utile.
DW

Immagino che questo non funzionerà se uno qualsiasi dei parametri ha 'carattere in esso? Esempio:callJavascript(mBrowser, "can't do it");
Kostanos il

18

Sì, hai l'errore di sintassi. Se si desidera ottenere gli errori Javascript e le istruzioni di stampa nel logcat, è necessario implementare il onConsoleMessage(ConsoleMessage cm)metodo in WebChromeClient. Fornisce le tracce complete dello stack come la console Web (elemento Inspect). Ecco il metodo

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

Dopo l'implementazione riceverai i tuoi errori Javascript e le istruzioni di stampa ( console.log) sul tuo logcat.


2
Credo che sia su WebChromeClient e non su WebViewClient.
Michael Levy,

Sì, hai ragione @MichaelLevy. Grazie per avermi indicato per quell'errore. Ora ho modificato la mia risposta.
Dinesh

1
Grazie, la tua risposta è stata proprio quello di cui avevo bisogno. Suggerirei anche alle persone di eseguire l'override di JsAlert () in WebChromeClient. La combinazione di registrazione javascript e avvisi può essere davvero utile quando si esegue il debug di JavaScript ospitato nella visualizzazione Web.
Michael Levy,

13

Modifica della risposta @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);
    }

creerà correttamente chiamate JS ad es

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

Si noti che gli oggetti non verranno passati correttamente, ma è possibile serializzarli prima di passare come argomento.

Inoltre ho modificato la destinazione dell'errore, l'ho trasferito nel registro della console che può essere ascoltato da:

    webView.setWebChromeClient(new CustomWebChromeClient());

e cliente

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

Grazie. Questo metodo sfuggirà al carattere all'interno di qualsiasi variabile? In altre parole, funzionerà con callJavascript(mBrowser, "can't do it");:?
Kostanos,

1
Ho appena modificato la risposta e aggiunto la via di fuga al personaggio
Kostanos il

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

file delle risorse

<!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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.