Quiero obtener un valor de retorno de Javascript en Android. Puedo hacerlo con el iPhone, pero no con Android. Usé loadUrl, pero devolvió void en lugar de un objeto. Alguien puede ayudarme?
82
Igual que Keith
pero respuesta más corta
webView.addJavascriptInterface(this, "android");
webView.loadUrl("javascript:android.onData(functionThatReturnsSomething)");
E implementar la función
@JavascriptInterface
public void onData(String value) {
//.. do something with the data
}
No se olvide de quitar el onData
de proguard
la lista (si se ha habilitado Proguard)
proguard
parte?document.getElementById("my-div").scrollTop
obtener la posición de desplazamiento desdeSwipeRefreshLayout::canChildScrollUp()
. Sospecho que podría deberse a que la llamada se llama demasiadas veces en un período corto. oproguard
lo que sospecho porque simplemente no sé qué es ..Aquí hay un truco sobre cómo puede lograrlo:
Agregue este cliente a su 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; } }
Ahora en su llamada javascript haga:
webView.loadUrl("javascript:alert(functionThatReturnsSomething)");
Ahora, en la llamada onJsAlert, el "mensaje" contendrá el valor devuelto.
fuente
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
lugar deonJsAlert
y luego en la llamada de JavaScript que hacewebView.loadUrl("javascript:console.log(functionThatReturnsSomething)");
; de esa manera, deja las alertas de JS en paz.WebChromeClient.onConsoleMessage
parece ser más limpia, pero tenga en cuenta que no funciona con algunos dispositivos HTC. Consulte esta pregunta para obtener más información.mWebView.addJavascriptInterface(YourObject mYourObject,String yourNameForObject);
llamando a la funciónmWebView.loadUrl("javascript:yourNameForObject.yourSetterMethod(valueToSet)")
Úselo
addJavascriptInterface()
para agregar un objeto Java al entorno Javascript. Haga que su Javascript llame a un método en ese objeto Java para proporcionar su "valor de retorno".fuente
loadUrl("javascript:...")
(API nivel 1-18) oevaluateJavascript()
(API nivel 19+) para evaluar su propio JavaScript en el contexto de la página web cargada actualmente.Esto es lo que se me ocurrió hoy. Es seguro para subprocesos, razonablemente eficiente y permite la ejecución síncrona de Javascript desde Java para un WebView de Android .
Funciona en Android 2.2 y versiones posteriores. (Requiere commons-lang porque necesito que mis fragmentos de código pasen a eval () como una cadena de JavaScript. Puede eliminar esta dependencia envolviendo el código no entre comillas, sino en
function(){}
)Primero, agregue esto a su archivo 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); }
Luego, agregue esto a su actividad de 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); } }
fuente
function(){}
? ¿Cómo colocar jsString en{}
?En API 19+, la mejor manera de hacer esto es llamar evaluateJavascript en su vista web:
webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { // value is the result returned by the Javascript as JSON } });
Respuesta relacionada con más detalles: https://stackoverflow.com/a/20377857
fuente
La solución que sugirió @Felix Khazin funciona, pero falta un punto clave.
La llamada javascript debe realizarse después de que
WebView
se cargue la página web en el . Agregue estoWebViewClient
alWebView
, junto con elWebChromeClient
.Ejemplo completo:
@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; } }
fuente
Como variante alternativa que usa un esquema personalizado:
Actividad principal
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 (ubicado en los activos plegados)
<!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>
fuente
Puedes hacerlo así:
[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); } }
fuente