Android Calling JavaScript функції в WebView


249

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

У javascriptфункції виглядає наступним чином :

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

У веб-перегляді я намагався викликати 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, може бути проблема?


13
Код, який ви намагаєтеся використовувати, пропускає котирування на стороні javascript.
Пол де Врієзе

2
Зауважте, що оскільки для Android 4.2 вам потрібно використовувати @JavascriptInterfaceдекоратор у методах Java, які ви хочете зробити доступними для веб-перегляду через інтерфейс JavaScript.
Фред

1
З коду myWebView.loadUrl("javascript:testEcho('Hello World!')");я розумію, що ви вже додали файл HTML до цього веб-перегляду. Не могли б ви сказати мені, як ви це зробили?
Tony_craft

Відповіді:


195

Я зрозумів, у чому проблема: відсутні лапки в параметрі testEcho (). Ось як я отримав дзвінок на роботу:

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

будь ласка , загляньте в цьому посиланню 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 є метод оцінювання JavaScript, перевірити stackoverflow.com/a/32163655/3063226
Heitor

117

Від kitkat і далі використовуйте метод evaluJavascript замість 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
чому використовувати оценкуJavascript () замість loadUrl ()?
Kamran Bigdely

2
@kami loadUrl () перезавантажить сторінку та знову зателефонує на сторінкуPageFinished ()
Зак

1
@Zach Це все ще проблема попереднього KitKat, чи не так?
Всеволод Ганін

2
@kami Додавши відповідь Заха, оцінювати JavaScript (), запускатиме JavaScript асинхронно. Таким чином, ми можемо уникати блокування потоку інтерфейсу, використовуючи AsseJa JavaScript ().
Прасад

loadUrlтакож спричинила появу клавіатури програмного забезпечення, коли я намагаюся заповнити значення в полі введення.
Джошуа

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

і якщо це не працює, зробіть нову тему (новий Runnaable () {.. <як вище> ..}). start ()
Radu Simionescu

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);}
Лукаш 'Северіан' Грела

Навіть простий дзвінок callJavaScript(mBrowser, "alert", "abc", "def");створює помилку, оскільки в кінці завжди є розбіжна кома. -1 від мене.
Лукаш 'Северіан' Грела

1
@ Lukasz'Severiaan'Grela tnx, я це виправив. PS Наступного разу не лінуйтеся, ви можете самі це виправити ...;)
Ілля Газман,

16
Для подальшої довідки: Будьмо позитивними. Вказівка ​​на помилку в коді не лінива. Це все ще корисні відгуки.
DW

Я думаю, це не спрацює, якщо будь-який з параметрів має в ньому символ? Приклад:callJavascript(mBrowser, "can't do it");
Костанос

18

Так, у вас є синтаксична помилка. Якщо ви хочете, щоб ваші помилки Javascript та друковані висловлювання були у вашій реєстраційній системі, ви повинні реалізувати 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) на вашій реєстраційній системі.


2
Я вважаю, що це є на WebChromeClient, а не на WebViewClient.
Майкл Леві

Так, ти прав @MichaelLevy. Дякую, що вказали на мене за цю помилку. Тепер я відредагував свою відповідь.
Дінеш

1
Спасибі, ваша відповідь була саме тим, що мені було потрібно. Я б також запропонував людям замінити наJsAlert () в 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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.