Android Вызов функций JavaScript в WebView

249

Я пытаюсь вызвать некоторые 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"); 

И вот 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, могут ли они быть проблемой?

user163757
источник
13
Код, который вы пытаетесь использовать, пропускает кавычки на стороне JavaScript.
Поль де Вриз
2
Обратите внимание, что начиная с Android 4.2 вам необходимо использовать @JavascriptInterfaceдекоратор в методах Java, которые вы хотите сделать доступными для WebView через интерфейс JavaScript.
Фред
1
Из кода myWebView.loadUrl("javascript:testEcho('Hello World!')");я понимаю, что вы уже прикрепили HTML-файл к этому веб-представлению. Не могли бы вы сказать мне, как ты это сделал?
Tony_craft

Ответы:

195

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

myWebView.loadUrl("javascript:testEcho('Hello World!')");
user163757
источник
Посмотрите, пожалуйста, на эту ссылку stackoverflow.com/questions/9079374/…
kamal_tech_view
Это хорошо, но как передать объект Java функции JavaScript в качестве аргумента?
Ковач Имре
1
@ KovácsImre String.Format ("javascript: testEcho ("% d ","% d ","% d ")", 1, 2, 3); Я думаю
user457015
Спасибо, я также узнал в то же время.
Ковач Имре
1
Начиная с KitKat, существует методvaluJavascript, проверьте stackoverflow.com/a/32163655/3063226
Heitor
117

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

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        webView.evaluateJavascript("enable();", null);
    } else {
        webView.loadUrl("javascript:enable();");
    }
Прасад
источник
2
зачем использовать valuJavascript () вместо loadUrl ()?
Камран Бигдели
2
@kami loadUrl () перезагрузит страницу и снова вызовет onPageFinished ()
Зак
1
@ Zach Это все еще проблема pre-KitKat, не так ли?
Всеволод Ганин
2
@kami При добавлении ответа в Zach ,valuJavascript (), JavaScript будет работать асинхронно. Таким образом, мы можем избежать блокирования потока пользовательского интерфейса с помощьюvaluJavascript ().
Прасад
loadUrlтакже вызвал появление программной клавиатуры при попытке ввести значение в поле ввода.
Джошуа
34
public void run(final String scriptSrc) { 
        webView.post(new Runnable() {
            @Override
            public void run() { 
                webView.loadUrl("javascript:" + scriptSrc); 
            }
        }); 
    }
user340994
источник
и если это не сработает, создайте новый поток (new Runnaable () {.. <как указано выше> ..}). start ()
Раду Симионеску
26

Я создал хорошую оболочку для вызова методов 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");
Илья Газман
источник
Эта функция, как бы хороша она ни была, имеет неверную реализацию. Этот вызов: final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a);уступят SyntaxErrorс Unexpected tokenкоторой не удивительно , когда вы будете смотреть на сгенерированных JS называют:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
Лукаш «Severiaan» Grela
Даже простой вызов callJavaScript(mBrowser, "alert", "abc", "def");генерирует ошибку, так как в конце всегда есть мошенническая запятая. -1 от меня.
Лукаш 'Severiaan' Grela
1
@ Lukasz'Severiaan'Grela tnx, я исправил это. PS В следующий раз не ленись, ты сам можешь это исправить ...;)
Илья Газман
16
Для дальнейшего использования: давайте оставаться позитивными. Указывать на ошибку в коде не лениво. Это все еще полезная обратная связь.
DW
Я думаю, это не будет работать, если какой-либо параметр имеет символ 'в нем? Пример:callJavascript(mBrowser, "can't do it");
Костанос
18

Да, у вас есть синтаксическая ошибка. Если вы хотите получать ошибки 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.

Динеш
источник
2
Я считаю, что это на WebChromeClient, а не WebViewClient.
Майкл Леви
Да, вы правы @MichaelLevy. Спасибо, что указал мне на эту ошибку. Теперь я отредактировал свой ответ.
Динеш
1
Спасибо, твой ответ был именно тем, что мне было нужно. Я бы также предложил людям переопределить onJsAlert () в WebChromeClient. Комбинация регистрации javascript и предупреждений может быть очень полезна при отладке Javascript, размещенного в веб-представлении.
Майкл Леви
13

Модификация ответа @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;
    }
}
Лукаш 'Северянин' Грела
источник
Спасибо. Будет ли этот метод экранировать символ внутри любой переменной? Другими словами, он будет работать с: callJavascript(mBrowser, "can't do it");?
Костанос
1
Просто отредактировал ответ и добавил побег к персонажу
Костанос
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')");
            }
        });
    }
}

файл активов

<!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>
Дариуш
источник