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.