Android Calling JavaScript функции в WebView

Я пытаюсь вызвать некоторые функции javascript, сидящие на странице html, запущенной внутри android webview. Довольно просто, что код пытается сделать ниже - из приложения Android, вызовите функцию javascript с тестовым сообщением, которое inturn вызывает функцию java обратно в приложении Android, которое отображает тестовое сообщение через тост.

Функция javascript выглядит так:

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

Из WebView я попытался вызвать javascript следующие способы без везения:

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

Я включил javascript в WebView

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

И heres Java Класс

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

Я потратил много времени на поиски, чтобы понять, что я могу делать неправильно. Все примеры, которые я нашел, используют этот подход. Кто-нибудь видит здесь что-то не так?

Изменить: Есть несколько других внешних файлов javascript, на которые ссылаются и используются в html, могут ли они быть проблемой?

Ответ 1

Я понял, в чем проблема: отсутствующие кавычки в параметре testEcho(). Вот как я получил звонок на работу:

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

Ответ 2

Из kitkat и далее используйте метод javascript вместо loadUrl, чтобы вызвать функции javascript, как показано ниже

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

Ответ 3

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

Ответ 4

Я создал хорошую оболочку для вызова методов JavaScript; это также показывает ошибки JavaScript в журнале:

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

Вы должны добавить это тоже:

private class WebViewInterface{

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

И добавьте этот интерфейс в ваше веб-представление:

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

Ответ 5

Да, у вас есть синтаксическая ошибка. Если вы хотите получать ошибки Javascript и операторы печати в logcat, вы должны реализовать метод onConsoleMessage(ConsoleMessage cm) в вашем WebChromeClient. Он дает полные трассировки стека, такие как веб-консоль (элемент Inspect). Вот метод.

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

После реализации вы получите ваши ошибки Javascript и напечатаете операторы (console.log) на вашем logcat.

Ответ 6

Модификация ответа @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);
    }

будет правильно создавать вызовы JS, например

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

Обратите внимание, что объекты не будут переданы правильно - но вы можете сериализовать их перед передачей в качестве аргумента.

Кроме того, я изменил, где идет ошибка, я перенаправил ее в журнал консоли, который можно прослушать:

    webView.setWebChromeClient(new CustomWebChromeClient());

и клиент

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