Android 4.4 (API nivel 19) presenta una nueva versión de WebView
basada en Chromium. Este cambio actualiza el rendimiento de WebView
y la compatibilidad de los estándares para HTML5, CSS3 y JavaScript a fin de que coincidan con los navegadores web más recientes. Las apps que usen WebView
heredarán estas actualizaciones cuando se ejecuten en Android 4.4 y versiones posteriores.
En este documento, se describen los cambios adicionales a WebView
que debes tener en cuenta si estableces tu targetSdkVersion
en "19" o una versión posterior.
Nota: Si tu targetSdkVersion
está configurada en "18" o versiones anteriores, WebView
funciona en "modo no estándar" para evitar algunos de los cambios de comportamiento que se describen a continuación, lo más detalladamente posible, al tiempo que sigue proporcionando a tu app las actualizaciones de rendimiento y de estándares web.
Sin embargo, ten en cuenta que los diseños de columna única y estrecha y los niveles de zoom predeterminados no se admiten en Android 4.4, y puede haber otras diferencias de comportamiento no identificadas. Por lo tanto, asegúrate de probar la app en Android 4.4 o versiones posteriores, incluso si mantienes tu targetSdkVersion
configurada en "18" o versiones anteriores.
Para solucionar posibles problemas al migrar tu app a WebView
en Android 4.4., puedes habilitar la depuración remota mediante Chrome en la computadora de escritorio llamando a setWebContentsDebuggingEnabled()
.
Esta nueva función en WebView
te permite inspeccionar y analizar el contenido web, las secuencias de comandos y la actividad de red mientras se ejecutan en una WebView
. Para obtener más información, consulta el artículo Depuración remota en Android.
Cambios del usuario-agente
Si publicas contenido en tu WebView
en función del usuario-agente, debes tener en cuenta que la string del usuario-agente tuvo algunos cambios menores y ahora incluye la versión de Chrome:
Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
Si debes recuperar el usuario-agente, pero no necesitas almacenarlo para tu app o no quieres crear una instancia de WebView
, tienes que usar el método estático getDefaultUserAgent()
. No obstante, si planeas anular la string del usuario-agente en tu WebView
, quizás te convenga usar getUserAgentString()
.
Bloqueo de subprocesos y subprocesos múltiples
Si llamas a métodos de WebView
desde cualquier subproceso que no sea el subproceso de IU de tu app, pueden generarse resultados inesperados. Por ejemplo, si la app usa subprocesos múltiples, puedes utilizar el método runOnUiThread()
para asegurarte de que tu código se ejecute en el subproceso de IU:
Kotlin
runOnUiThread { // Code for WebView goes here }
Java
runOnUiThread(new Runnable() { @Override public void run() { // Code for WebView goes here } });
También asegúrate de no bloquear el subproceso de IU en ningún caso. En algunas apps se produce este error mientras esperan una devolución de llamada de JavaScript. Por ejemplo, no uses un código como este:
Kotlin
// This code is BAD and will block the UI thread webView.loadUrl("javascript:fn()") while (result == null) { Thread.sleep(100) }
Java
// This code is BAD and will block the UI thread webView.loadUrl("javascript:fn()"); while(result == null) { Thread.sleep(100); }
En cambio, puedes usar un método nuevo, evaluateJavascript()
, para ejecutar JavaScript de manera asíncrona.
Cómo manejar URL personalizadas
La nueva WebView
aplica restricciones adicionales cuando solicita recursos y resuelve vínculos que utilizan un esquema de URL personalizada. Por ejemplo, si implementas devoluciones de llamada como shouldOverrideUrlLoading()
o shouldInterceptRequest()
, WebView
las invoca solo para URL válidas.
Si usas un esquema de URL personalizada o una URL base y notas que tu app recibe menos llamadas a estas devoluciones de llamada o no carga recursos en Android 4.4, asegúrate de que las solicitudes especifiquen URL válidas que cumplan con la especificación RFC 3986.
Por ejemplo, es posible que la nueva WebView
no llame a tu método shouldOverrideUrlLoading()
para vínculos como el siguiente:
<a href="showProfile">Show Profile</a>
El resultado de que el usuario haga clic en dicho vínculo puede variar:
- Si cargaste la página llamando a
loadData()
oloadDataWithBaseURL()
con una URL base no válida o nula, no recibirás la devolución de llamadashouldOverrideUrlLoading()
para este tipo de vínculo en la página.Nota: Cuando uses
loadDataWithBaseURL()
y la URL base no sea válida o esté establecida como nula, todos los vínculos del contenido que cargues deberán ser absolutos. - Si llamaste a
loadUrl()
para cargar la página o proporcionaste una URL base válida conloadDataWithBaseURL()
, recibirás la devolución de llamadashouldOverrideUrlLoading()
para este tipo de vínculo en la página, pero la URL que recibirás será absoluta y corresponderá a la página actual. Por ejemplo, la URL que recibas será"http://www.example.com/showProfile"
en lugar de solo"showProfile"
.
En lugar de usar una string simple en un vínculo como se muestra más arriba, puedes utilizar un esquema personalizado como el siguiente:
<a href="example-app:showProfile">Show Profile</a>
Luego, puedes manejar esta URL en tu método shouldOverrideUrlLoading()
de la siguiente manera:
Kotlin
// The URL scheme should be non-hierarchical (no trailing slashes) const val APP_SCHEME = "example-app:" override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { return if (url?.startsWith(APP_SCHEME) == true) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8") respondToData(urlData) true } else { false } }
Java
// The URL scheme should be non-hierarchical (no trailing slashes) private static final String APP_SCHEME = "example-app:"; @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith(APP_SCHEME)) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8"); respondToData(urlData); return true; } return false; }
Si no puedes modificar el código HTML, tal vez puedas usar loadDataWithBaseURL()
y establecer una URL base que conste de un esquema personalizado y un host válido, como "example-app://<valid_host_name>/"
. Por ejemplo:
Kotlin
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA, null, "UTF-8", null)
Java
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA, null, "UTF-8", null);
El nombre de host válido debe cumplir con la especificación RFC 3986. Es importante incluir la barra final, ya que, de lo contrario, las solicitudes de la página cargada podrían perderse.
Cambios del viewport
Ya no se admite la propiedad target-densitydpi del viewport
Anteriormente, WebView
admitía una propiedad del viewport llamada target-densitydpi
para ayudar a las páginas web a especificar la densidad de pantalla deseada. Esta propiedad ya no es compatible y debes migrar al uso de soluciones estándar con imágenes y CSS como se describe en el artículo IU de Pixel Perfect en WebView.
El viewport hace un acercamiento cuando es pequeño
Anteriormente, si configurabas el ancho del viewport en un valor inferior o igual a "320", se asignaba a "device-width" y, si configurabas el alto en un valor inferior o igual al alto de la WebView
, se asignaba a "device-height". Sin embargo, cuando se ejecuta en la nueva WebView
, se respeta el valor de ancho o alto y la WebView
hace un acercamiento para llenar el ancho de la pantalla.
No se admiten múltiples etiquetas de viewport
Anteriormente, si incluías varias etiquetas de viewport en una página web, WebView
fusionaba las propiedades de todas las etiquetas.
En la nueva WebView
, solo se usa el último viewport y se ignoran todos los demás.
El zoom predeterminado dejó de estar disponible
Ya no se admiten los métodos getDefaultZoom()
y setDefaultZoom()
para obtener y establecer el nivel de zoom inicial en una página. En cambio, debes definir el viewport adecuado en la página web.
Precaución: Estas API no se admiten en Android 4.4 y versiones posteriores. Incluso aunque tu targetSdkVersion
esté configurada en "18" o versiones anteriores, las API no tendrán efecto.
Para obtener información sobre cómo definir las propiedades del viewport en el código HTML, consulta el artículo IU de Pixel Perfect en WebView.
Si no puedes establecer el ancho del viewport en el código HTML, debes llamar a setUseWideViewPort()
para asegurarte de que la página tenga un viewport más amplio. Por ejemplo:
Kotlin
webView.settings.apply { useWideViewPort = true loadWithOverviewMode = true }
Java
WebSettings settings = webView.getSettings(); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true);
Cambios de estilo
La propiedad abreviada de CSS background anula la propiedad background-size
Chrome y otros navegadores se comportaron de esta manera durante un tiempo, pero ahora WebView
anulará además una configuración de CSS para background-size
si también especificas el estilo background
. Por ejemplo, aquí el tamaño se restablecerá a un valor predeterminado:
.some-class { background-size: contain; background: url('images/image.png') no-repeat; }
Para solucionarlo, solo hay que intercambiar las dos propiedades.
.some-class { background: url('images/image.png') no-repeat; background-size: contain; }
Los tamaños están expresados en píxeles de CSS y no en píxeles de pantalla
Anteriormente, los parámetros de tamaño, como window.outerWidth
y window.outerHeight
, mostraban un valor en píxeles de pantalla reales.
En la nueva WebView
, muestran un valor basado en píxeles de CSS.
No se considera buena práctica intentar calcular el tamaño físico en píxeles para determinar el tamaño de los elementos u otros valores. Sin embargo, si inhabilitaste el zoom y la escala inicial está configurada en 1.0, puedes usar window.devicePixelRatio
para obtener la escala y luego multiplicarla por el valor de píxeles de CSS. Como alternativa, puedes crear una vinculación de JavaScript para buscar el tamaño de píxeles desde la WebView
.
Para obtener más información, consulta el sitio quirksmode.org.
NARROW_COLUMNS y SINGLE_COLUMN ya no son compatibles
El valor NARROW_COLUMNS
para WebSettings.LayoutAlgorithm
no se admite en la nueva WebView
.
Precaución: Estas API no se admiten en Android 4.4 y versiones posteriores. Incluso aunque tu targetSdkVersion
esté configurada en "18" o versiones anteriores, las API no tendrán efecto.
Puedes manejar el cambio de las siguientes maneras:
- Modifica los estilos de tu aplicación:
Si controlas el código HTML y CSS en la página, es posible que alterar el diseño del contenido sea el enfoque más confiable. Por ejemplo, para las pantallas donde citas licencias, quizás quieras unir el texto dentro de una etiqueta
<pre>
, algo que puedes lograr con los siguientes estilos:<pre style="word-wrap: break-word; white-space: pre-wrap;">
Esta acción puede ser útil en especial si no definiste las propiedades del viewport para tu página.
- Usa el nuevo algoritmo de diseño
TEXT_AUTOSIZING
:Si utilizabas columnas estrechas como una forma de hacer que un amplio espectro de sitios de escritorio fueran más legibles en dispositivos móviles y no puedes cambiar el contenido HTML, el nuevo algoritmo
TEXT_AUTOSIZING
puede ser una alternativa adecuada aNARROW_COLUMNS
.
Además, el valor SINGLE_COLUMN
, que ya estaba obsoleto, tampoco se admite en la nueva WebView
.
Cómo manejar eventos táctiles en JavaScript
Si tu página web maneja directamente los eventos táctiles en una WebView
, asegúrate de manejar también el evento touchcancel
. Hay algunos casos en los que se llamará a touchcancel
, lo que puede causar problemas si no se recibe:
- Se toca un elemento (entonces se llama a
touchstart
y atouchmove
) y la página se desplaza, lo que da lugar atouchcancel
. - Se toca un elemento (se llama a
touchstart
), pero no se llama aevent.preventDefault()
, lo que da lugar atouchcancel
con suficiente antelación (por lo queWebView
presupone que no deseas consumir los eventos táctiles).