Как работает Javascript?

Я пытаюсь использовать новый метод valuJavascript в 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
    }
});

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

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

  1. У меня есть набор разрешений ИНТЕРНЕТ
  2. я имею setJavascriptEnabled(true);
  3. Пробовал апострофную строку: return 'test';,
  4. Испытанный объект JS: return { test: 'this' }
  5. console.log('test'); выполняется хорошо.
  6. Задавать targetSdkVersion до 19 согласно: если ваше приложение использует WebView

Устройства: как Nexus 7, так и Nexus 5 (сток)

4 ответа

Решение

Ниже приведен пример использования метода valuJavascript в этом примере:

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

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

Главное, на что нужно обратить внимание, это то, что String, возвращаемая в 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, заключенная в двойные кавычки: "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)
    }
});

Ок, так получается 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"}
    }
});

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

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

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

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

    }

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

Чтобы суммировать ответ @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
}

Важный совет:
перед звонкомevaluateJavascriptвам необходимо включить JavaScript для вашего WebView. Иначе результата не будет.

WebSettings settings = yourWebview.getSettings();
settings.setJavaScriptEnabled(true);
Другие вопросы по тегам