Как работает функция JavaScriptJavascript?

Я пытаюсь использовать новый метод evaluateJavascript в Android 4.4, но все, что я когда-либо возвращаю, - это нулевой результат:

webView1.evaluateJavascript("return \"test\";", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Log is written, but s is always null
    }
});

Как вернуть результат этому методу?

Обновление: Немного больше информации:

  • У меня есть набор разрешений INTERNET
  • У меня есть setJavascriptEnabled(true);
  • Исправлена ​​строка апострофа: return 'test';,
  • Исправленный объект JS: return { test: 'this' }
  • console.log('test'); выполняется нормально.
  • Установите targetSdkVersion в 19: Если ваше приложение использует WebView

Устройства: Оба Nexus 7 и Nexus 5 (Stock)

Ответ 1

В этом примере используется пример метода methodJavascript:

https://github.com/GoogleChrome/chromium-webview-samples/tree/master/jsinterface-example

По существу, если javascript, который вы выполняете в WebView, возвращает значение, которое будет передано в обратном вызове.

Главное отметить, что строка, возвращаемая в OnReceiveValue, представляет собой значение JSON Value, JSON Object или JSON Array в зависимости от того, что вы возвращаете.

Следует отметить, что если вы вернете одно значение, вам нужно использовать setLenient (true) в считывателе JSON для его работы.

     if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // In KitKat+ you should use the evaluateJavascript method
        mWebView.evaluateJavascript(javascript, new ValueCallback<String>() {
            @TargetApi(Build.VERSION_CODES.HONEYCOMB)
            @Override
            public void onReceiveValue(String s) {
                JsonReader reader = new JsonReader(new StringReader(s));

                // Must set lenient to parse single values
                reader.setLenient(true);

                try {
                    if(reader.peek() != JsonToken.NULL) {
                        if(reader.peek() == JsonToken.STRING) {
                            String msg = reader.nextString();
                            if(msg != null) {
                                Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
                            }
                        }
                    }
                } catch (IOException e) {
                    Log.e("TAG", "MainActivity: IOException", e);
                } finally {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        // NOOP
                    }
                }
            }
        });
    }

Причина, по которой вы все еще можете использовать синтаксический анализатор для строкового ответа, преобразуется в значение JSON, что означает, что он будет заключен в кавычки.

Например, если вы пошли:

mWebView.evaluateJavascript("(function() { return 'this'; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints: "this"
    }
});

Он напечатает эту строку, заключенную в двойные кавычки: "this".

Другие примеры, заслуживающие внимания:

mWebView.evaluateJavascript("(function() { return null; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints the string 'null' NOT Java null
    }
});

mWebView.evaluateJavascript("(function() { })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); //s is Java null
    }
});

mWebView.evaluateJavascript("(function() { return ''; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints "" (Two double quotes)
    }
});

Ответ 2

ОК, так что получается result здесь результат вызова Javascript - как если бы кто-то вводил эту команду в консоль Javascript.

Итак, чтобы получить результат, его нужно обернуть в функцию:

webView1.evaluateJavascript("(function() { return \"this\"; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints 'this'
    }
});

Это также будет работать:

webView1.evaluateJavascript("window.variable = \"asd\";", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints asd
    }
});

Метод также обрабатывает объекты Javascript:

webView1.evaluateJavascript("(function() { return { var1: \"variable1\", var2: \"variable2\" }; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s); // Prints: {"var1":"variable1","var2":"variable2"}
    }
});

Ответ 3

AndroidJSCore является хорошей альтернативой для оценки JavaScript, который не использует WebView.

Если вы хотите придерживаться WebView и вам нужно оценить JavaScript в более ранних версиях Android (4+), вот небольшая библиотека:

https://github.com/evgenyneu/js-evaluator-for-android

jsEvaluator.evaluate("put your JavaScript code", new JsCallback() {
  @Override
  public void onResult(final String result) {
    // get result here (optional)
  }
});

Ответ 4

Подводя итог ответу @GauntFace и предоставляем альтернативное решение без использования парсера JSON:

Если ваша функция JS возвращает только String, и вам интересно, почему эта строка искажена в Java, это потому, что она скрыта от JSON.

mWebView.evaluateJavascript("(function() { return 'Earvin \"Magic\" Johnson'; })();", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String s) {
        Log.d("LogName", s);
        // expected: s == Earvin "Magic" Johnson
        // actual:   s == "Earvin \"Magic\" Johnson"
    }
});

(обратите внимание, что onReceiveValue всегда предоставляет String, в то время как функция JS может возвращать null, литерал числа и т.д.)

Чтобы получить строковое значение так же, как в JS, если вы на 100% уверены, что ожидаете возврата String, вам нужно будет JSON-unescape, например:

String unescaped = s.substring(1, s.length() - 1)  // remove wrapping quotes
                     .replace("\\\\", "\\")        // unescape \\ -> \
                     .replace("\\\"", "\"");       // unescape \" -> "

Однако обратите внимание, что s может быть строкой "null", если JS возвращает правильный null, поэтому вам, очевидно, необходимо проверить это как самый первый шаг.

if ("null".equals(s)) {
   ...
} else {
   // unescape JSON
}

Ответ 5

Каждый ответ великолепен. Я просто добавлю еще один момент. Не помещайте valuJavascript внутри метода с аннотацией @JavascripInterface следующим образом

    @JavascripInterface
    public void onData(){ 
            mWebView.evaluateJavascript("javascript:executeNext()",null);
    }  

Так как он заблокирует поток JavaBridge. если вы хотите поместить в него defineJavascript. Сделай это так

    @JavascripInterface
    public void onData(){
        mWebView.post(new Runnable() {
            @Override
            public void run() {
                mWebView.evaluateJavascript("javascript:executeNext()",null);
            }
        });

    }