Как получить возвращаемое значение из JavaScript в веб-обозрение Android?
Я хочу получить возвращаемое значение из Javascript в Android. Я могу сделать это с iPhone, но я не могу с Android. Я использовал loadUrl, но он возвращал void вместо объекта. Кто-нибудь может мне помочь? Благодарю.
10 ответов
Такой же как Keith
но более короткий ответ
webView.addJavascriptInterface(this, "android");
webView.loadUrl("javascript:android.onData(functionThatReturnsSomething)");
И реализовать функцию
@JavascriptInterface
public void onData(String value) {
//.. do something with the data
}
Не забудьте удалить onData
от proguard
список (если вы включили Proguard)
Вот как мы можем это сделать:
Добавьте этот клиент в ваш WebView:
final class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
Теперь в вашем вызове JavaScript сделать:
webView.loadUrl("javascript:alert(functionThatReturnsSomething)");
Теперь в вызове onJsAlert "message" будет содержать возвращаемое значение.
Использование addJavascriptInterface()
добавить объект Java в среду Javascript. Пусть ваш Javascript вызовет метод для этого Java-объекта, чтобы предоставить его "возвращаемое значение".
В API 19+ лучший способ сделать это - вызвать методубследование JavaScript в вашем WebView:
webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() {
@Override public void onReceiveValue(String value) {
// value is the result returned by the Javascript as JSON
}
});
Связанный ответ с более подробной информацией: /questions/45623421/kak-rabotaet-javascript/45623475#45623475
Вот что я придумал сегодня. Это поточно-ориентированный, достаточно эффективный и допускающий синхронное выполнение Javascript из Java для Android WebView.
Работает в Android 2.2 и выше. (Требуется commons-lang, потому что мне нужно, чтобы мои фрагменты кода передавались в eval() в виде строки Javascript. Вы можете удалить эту зависимость, заключив код не в кавычки, а в function(){}
)
Сначала добавьте это в ваш файл Javascript:
function evalJsForAndroid(evalJs_index, jsString) {
var evalJs_result = "";
try {
evalJs_result = ""+eval(jsString);
} catch (e) {
console.log(e);
}
androidInterface.processReturnValue(evalJs_index, evalJs_result);
}
Затем добавьте это к своей активности Android:
private Handler handler = new Handler();
private final AtomicInteger evalJsIndex = new AtomicInteger(0);
private final Map<Integer, String> jsReturnValues = new HashMap<Integer, String>();
private final Object jsReturnValueLock = new Object();
private WebView webView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = (WebView) findViewById(R.id.webView);
webView.addJavascriptInterface(new MyJavascriptInterface(this), "androidInterface");
}
public String evalJs(final String js) {
final int index = evalJsIndex.incrementAndGet();
handler.post(new Runnable() {
public void run() {
webView.loadUrl("javascript:evalJsForAndroid(" + index + ", " +
"\"" + StringEscapeUtils.escapeEcmaScript(js) + "\")");
}
});
return waitForJsReturnValue(index, 10000);
}
private String waitForJsReturnValue(int index, int waitMs) {
long start = System.currentTimeMillis();
while (true) {
long elapsed = System.currentTimeMillis() - start;
if (elapsed > waitMs)
break;
synchronized (jsReturnValueLock) {
String value = jsReturnValues.remove(index);
if (value != null)
return value;
long toWait = waitMs - (System.currentTimeMillis() - start);
if (toWait > 0)
try {
jsReturnValueLock.wait(toWait);
} catch (InterruptedException e) {
break;
}
else
break;
}
}
Log.e("MyActivity", "Giving up; waited " + (waitMs/1000) + "sec for return value " + index);
return "";
}
private void processJsReturnValue(int index, String value) {
synchronized (jsReturnValueLock) {
jsReturnValues.put(index, value);
jsReturnValueLock.notifyAll();
}
}
private static class MyJavascriptInterface {
private MyActivity activity;
public MyJavascriptInterface(MyActivity activity) {
this.activity = activity;
}
// this annotation is required in Jelly Bean and later:
@JavascriptInterface
public void processReturnValue(int index, String value) {
activity.processJsReturnValue(index, value);
}
}
Решение, предложенное @Felix Khazin, работает, но отсутствует один ключевой момент.
Вызов JavaScript должен быть сделан после веб-страницы в WebView
загружен. Добавь это WebViewClient
к WebView
, вместе с WebChromeClient
,
Полный пример:
@Override
public void onCreate(Bundle savedInstanceState) {
...
WebView webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new MyWebViewClient());
webView.setWebChromeClient(new MyWebChromeClient());
webView.loadUrl("http://example.com");
}
private class MyWebViewClient extends WebViewClient {
@Override
public void onPageFinished (WebView view, String url){
view.loadUrl("javascript:alert(functionThatReturnsSomething())");
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
private class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
Как альтернативный вариант, который использует собственную схему:
Основная деятельность
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
final WebView webview = new CustomWebView(this);
setContentView(webview);
webview.loadUrl("file:///android_asset/customPage.html");
webview.postDelayed(new Runnable() {
@Override
public void run() {
//Android -> JS
webview.loadUrl("javascript:showToast()");
}
}, 1000);
}
}
CustomWebView
public class CustomWebView extends WebView {
public CustomWebView(Context context) {
super(context);
setup();
}
@SuppressLint("SetJavaScriptEnabled")
private void setup() {
setWebViewClient(new AdWebViewClient());
getSettings().setJavaScriptEnabled(true);
}
private class AdWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("customschema://")) {
//parse uri
Toast.makeText(CustomWebView.this.getContext(), "event was received", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
}
}
customPage.html (находится в сложенном виде)
<!DOCTYPE html>
<html>
<head>
<title>JavaScript View</title>
<script type="text/javascript">
<!--JS -> Android-->
function showToast() {
window.location = "customschema://goto/";
}
</script>
</head>
<body>
</body>
</html>
Сделать это можно так:
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public WebView web_view;
public static TextView textView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Window.AddFlags(WindowManagerFlags.Fullscreen);
Window.ClearFlags(WindowManagerFlags.ForceNotFullscreen);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.main);
web_view = FindViewById<WebView>(Resource.Id.webView);
textView = FindViewById<TextView>(Resource.Id.textView);
web_view.Settings.JavaScriptEnabled = true;
web_view.SetWebViewClient(new SMOSWebViewClient());
web_view.LoadUrl("https://stns.egyptair.com");
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public class SMOSWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return false;
}
public override void OnPageFinished(WebView view, string url)
{
view.EvaluateJavascript("document.getElementsByClassName('notf')[0].innerHTML;", new JavascriptResult());
}
}
public class JavascriptResult : Java.Lang.Object, IValueCallback
{
public string Result;
public void OnReceiveValue(Java.Lang.Object result)
{
string json = ((Java.Lang.String)result).ToString();
Result = json;
MainActivity.textView.Text = Result.Replace("\"", string.Empty);
}
}
Если вы хотите получить значение синхронно, вы должны знать, что webView.evaluateJavascript() работает асинхронно, но вы можете использовать простой семафор для ожидания выполнения.
Шаги:
Создайте интерфейс JavaScript (по сути, класс с методом, который будет вызываться из JavaScript, который мы хотим оценить.
public class MyInterface { private String value; private Semaphore semaphore; public MyInterface(Semaphore semaphore) { this.semaphore = semaphore; } public String getValue() { return value; } @JavascriptInterface public void setValue(String value) { this.value = value; //important release the semaphore after the execution semaphore.release(); } }
Добавьте интерфейс JavaScript в веб-представление Android.
Semaphore mySemaphore = new Semaphore(0); MyInterface myJsInterface = new MyInterface(mySemaphore); webView.addJavascriptInterface(myJsInterface, "jsInterface");
Запустите javascript и дождитесь выполнения
webView.evaluateJavascript("var myValue='this works';jsInterface.setValue(myValue);", null); //await the execution mySemaphore.acquire(); //the interface now have the value after the execution myJsInterface.getValue();
Чтобы получить данные из WebView в Android, вы можете использовать этот метод для выполнения кода JavaScript, который извлекает данные с веб-страницы. Вот пример:
WebView webView = findViewById(R.id.webview);webView.getSettings().setJavaScriptEnabled(истина); webView.loadUrl("https://example.com");
String js = "(function() { " +
"return document.getElementById('my_element').innerHTML;" +
"})()";
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
view.evaluateJavascript(js, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
// 'value' contains the result of the JavaScript code
Log.d("WebView", "Data: " + value);
}
});
}
});
В этом примере код JavaScript извлекает внутренний HTML-код элемента с идентификатором my_element . evaluateJavascript()
метод выполняет код и передает результат вValueCallback
который записывает данные на консоль. Вы можете изменить код JavaScript для получения различных типов данных, таких как входные значения формы или атрибуты элементов HTML. Просто убедитесь, что данные возвращаются в виде строки и анализируются вonReceiveValue()
метод при необходимости.