Cómo crear aplicaciones web en WebView

Si quieres publicar una aplicación web (o solo una página web) como parte de una aplicación cliente, puedes usar WebView para hacerlo. La clase WebView es una extensión de la clase View de Android que te permite mostrar páginas web como parte del diseño de tu actividad. No incluye las funciones de un navegador web completamente desarrollado, como controles de navegación o una barra de direcciones. De forma predeterminada, todo lo que hace WebView es mostrar una página web.

Una situación común en la que sirve usar WebView es cuando, en tu app, deseas proporcionar información que tal vez debas actualizar, como en un acuerdo de usuario final o una guía del usuario. Dentro de la app para Android, puedes crear una Activity que contenga una WebView y, luego, usarla para mostrar el documento que está alojado en línea.

Otra situación en la que WebView puede resultar útil es si tu app proporciona al usuario datos que siempre requieren una conexión a Internet para recuperar información, como el correo electrónico. En ese caso, en lugar de realizar una solicitud de red, analizar los datos y renderizarlos en un diseño de Android, posiblemente sea más fácil crear una WebView en tu app para Android que muestre una página web con todos los datos del usuario. Puedes diseñar una página web específica para dispositivos Android y, luego, implementar una WebView en tu app para Android que cargue esa página.

En este documento, se muestra cómo comenzar a usar WebView y realizar algunas tareas adicionales, por ejemplo, manejar la navegación de páginas y vincular JavaScript desde tu página web al código del cliente en la app para Android.

Cómo agregar una WebView a tu app

Para agregar una WebView a tu app, puedes incluir el elemento <WebView> en el diseño de la actividad o configurar toda la ventana de la actividad como una WebView en onCreate().

Cómo agregar una WebView en el diseño de la actividad

Para agregar una WebView a tu app en el diseño, incluye el siguiente código en el archivo XML del diseño de la actividad:

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
    

Para cargar una página web en la WebView, usa loadUrl(). Por ejemplo:

Kotlin

    val myWebView: WebView = findViewById(R.id.webview)
    myWebView.loadUrl("http://www.example.com")
    

Java

    WebView myWebView = (WebView) findViewById(R.id.webview);
    myWebView.loadUrl("http://www.example.com");
    

Cómo agregar una WebView en onCreate()

Para agregar una WebView a tu app en el método onCreate() de una actividad, utiliza una lógica similar a la siguiente:

Kotlin

    val myWebView = WebView(activityContext)
    setContentView(myWebView)
    

Java

    WebView myWebView = new WebView(activityContext);
    setContentView(myWebView);
    

A continuación, carga la página con este código:

Kotlin

    myWebView.loadUrl("http://www.example.com")
    

Java

    myWebView.loadUrl("https://www.example.com");
    

O carga la URL desde una string HTML:

Kotlin

    // Create an unencoded HTML string
    // then convert the unencoded HTML string into bytes, encode
    // it with Base64, and load the data.
    val unencodedHtml =
            "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;"
    val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
    myWebView.loadData(encodedHtml, "text/html", "base64")
    

Java

    // Create an unencoded HTML string
    // then convert the unencoded HTML string into bytes, encode
    // it with Base64, and load the data.
    String unencodedHtml =
         "&lt;html&gt;&lt;body&gt;'%23' is the percent code for ‘#‘ &lt;/body&gt;&lt;/html&gt;";
    String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
            Base64.NO_PADDING);
    myWebView.loadData(encodedHtml, "text/html", "base64");
    

Nota: Esta string HTML tiene algunas restricciones. Consulta los detalles sobre loadData() y loadDataWithBaseURL() para obtener más información acerca de las opciones de codificación.

Sin embargo, para que esto funcione, la app debe tener acceso a Internet. Para obtenerlo, solicita el permiso INTERNET en tu archivo de manifiesto. Por ejemplo:

    <manifest ... >
        <uses-permission android:name="android.permission.INTERNET" />
        ...
    </manifest>
    

Eso es todo lo que necesitas para una WebView básica que muestre una página web. Además, puedes personalizar tu WebView mediante las siguientes modificaciones:

  • Habilita la compatibilidad con la pantalla completa empleando WebChromeClient. También se llama a esta clase cuando una WebView necesita permiso para hacer modificaciones en la IU de la app host, como crear o cerrar ventanas y enviar diálogos de JavaScript al usuario. Si deseas obtener más información sobre la depuración en este contexto, consulta el artículo Depuración de aplicaciones web.
  • Maneja los eventos que afectan el procesamiento del contenido, como los errores en el envío de formularios o la navegación con WebViewClient. También puedes usar esta subclase para interceptar la carga de URL.
  • Habilita JavaScript modificando WebSettings.
  • Usa JavaScript para acceder a los objetos del marco de trabajo de Android que insertaste en una WebView.

Cómo usar JavaScript en WebView

Si la página web que planeas cargar en tu WebView usa JavaScript, debes habilitarlo para la . Una vez que JavaScript esté habilitado, también podrás crear interfaces entre el código de la app y el de JavaScript.

Cómo habilitar JavaScript

De forma predeterminada, JavaScript está inhabilitado en la WebView. Habilítalo a través del objeto WebSettings adjunto a tu WebView. Puedes recuperar el objeto WebSettings con getSettings() y habilitar JavaScript con setJavaScriptEnabled().

Por ejemplo:

Kotlin

    val myWebView: WebView = findViewById(R.id.webview)
    myWebView.settings.javaScriptEnabled = true
    

Java

    WebView myWebView = (WebView) findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    webSettings.setJavaScriptEnabled(true);
    

WebSettings proporciona acceso a otras opciones de configuración que podrían resultarte útiles. Por ejemplo, si estás desarrollando una aplicación web diseñada específicamente para la WebView en tu app de Android, puedes definir una string de usuario-agente personalizado con setUserAgentString() y, luego, buscar el usuario-agente personalizado en tu página web para verificar que el cliente que solicita la página web sea en efecto tu app de Android.

Cómo vincular el código de JavaScript al código de Android

Cuando desarrollas una aplicación web diseñada específicamente para la WebView en tu app de Android, puedes crear interfaces entre el código de JavaScript y el código de Android del cliente. Por ejemplo, el código de JavaScript puede llamar a un método en tu código de Android para mostrar un Dialog, en lugar de utilizar la función alert() de JavaScript.

A fin de vincular una nueva interfaz entre el código de JavaScript y el de Android, llama a addJavascriptInterface() y pásale una instancia de clase para vincularla al código de JavaScript y un nombre de interfaz al que pueda llamar el código para acceder a la clase.

Por ejemplo, puedes incluir la siguiente clase en tu app para Android:

Kotlin

    /** Instantiate the interface and set the context  */
    class WebAppInterface(private val mContext: Context) {

        /** Show a toast from the web page  */
        @JavascriptInterface
        fun showToast(toast: String) {
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
        }
    }
    

Java

    public class WebAppInterface {
        Context mContext;

        /** Instantiate the interface and set the context */
        WebAppInterface(Context c) {
            mContext = c;
        }

        /** Show a toast from the web page */
        @JavascriptInterface
        public void showToast(String toast) {
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
        }
    }
    

Precaución: Si configuraste la versión de tu targetSdkVersion en 17 o un valor posterior, debes agregar la anotación @JavascriptInterface a cualquier método que desees que esté disponible para el código de JavaScript (el método también debe ser público). Si no proporcionas la anotación, tu página web no podrá acceder al método cuando se ejecute en Android 4.2 o versiones posteriores.

En este ejemplo, la clase WebAppInterface permite que la página web cree un mensaje Toast mediante el método showToast().

Puedes vincular esta clase al código de JavaScript que se ejecuta en tu WebView con addJavascriptInterface() y asignar el nombre Android a la interfaz. Por ejemplo:

Kotlin

    val webView: WebView = findViewById(R.id.webview)
    webView.addJavascriptInterface(WebAppInterface(this), "Android")
    

Java

    WebView webView = (WebView) findViewById(R.id.webview);
    webView.addJavascriptInterface(new WebAppInterface(this), "Android");
    

Así se crea una interfaz llamada Android para el código de JavaScript que se ejecuta en la WebView. En este punto, tu aplicación web tiene acceso a la clase WebAppInterface. Por ejemplo, a continuación se muestran instrucciones de HTML y JavaScript que crean un mensaje de notificación usando la nueva interfaz cuando el usuario hace clic en un botón:

    <input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

    <script type="text/javascript">
        function showAndroidToast(toast) {
            Android.showToast(toast);
        }
    </script>
    

No es necesario inicializar la interfaz Android desde JavaScript. La WebView la pone automáticamente a disposición de tu página web. Entonces, cuando se hace clic en el botón, la función showAndroidToast() usa la interfaz Android para llamar al método WebAppInterface.showToast().

Nota: El objeto que está vinculado a tu código de JavaScript se ejecuta en otro subproceso y no en el que se construyó.

Precaución: Al usar addJavascriptInterface() permites que JavaScript controle tu app para Android. Esta puede ser una función muy útil o bien un importante riesgo de seguridad. Cuando el código de HTML de la WebView no es confiable (por ejemplo, si parte o la totalidad de ese código es provisto por una persona o proceso desconocidos), un atacante puede incluir HTML que ejecute tu código del cliente y posiblemente cualquier código que elija. Por lo tanto, no debes usar addJavascriptInterface() a menos que hayas escrito todo el código HTML y JavaScript que aparece en tu WebView. Tampoco debes permitir que el usuario navegue a otras páginas web que no sean tuyas dentro de tu WebView (en cambio, permite que la app del navegador predeterminada del usuario abra vínculos externos; de forma predeterminada, el navegador web del usuario abre todos los vínculos de URL, así que ten cuidado solo si manejas la navegación de páginas como se describe en la siguiente sección).

Cómo manejar la navegación de páginas

Cuando el usuario hace clic en un vínculo de una página web en tu WebView, según el comportamiento predeterminado Android inicia una app que maneja las URL. Por lo general, se abre el navegador web predeterminado y se carga la URL de destino. Sin embargo, puedes anular ese comportamiento para la WebView a fin de que los vínculos se abran dentro de tu WebView. Luego, puedes permitir que el usuario navegue hacia atrás y hacia adelante a través del historial de páginas web que mantiene tu WebView.

Nota: Por razones de seguridad, la aplicación del navegador del sistema no comparte los datos de la aplicación con tu app.

Para abrir vínculos en los que el usuario hizo clic, usa setWebViewClient() a fin de proporcionar un WebViewClient a tu WebView. Por ejemplo:

Kotlin

    val myWebView: WebView = findViewById(R.id.webview)
    myWebView.webViewClient = WebViewClient()
    

Java

    WebView myWebView = (WebView) findViewById(R.id.webview);
    myWebView.setWebViewClient(MyWebViewClient);
    

Eso es todo. Ahora, todos los vínculos en los que el usuario haya hecho clic se cargarán en tu WebView.

Si deseas tener más control sobre dónde se carga un vínculo en el que se hizo clic, crea tu propio WebViewClient que anule el método shouldOverrideUrlLoading(). Por ejemplo:

Kotlin

    private class MyWebViewClient : WebViewClient() {

        override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
            if (Uri.parse(url).host == "www.example.com") {
                // This is my web site, so do not override; let my WebView load the page
                return false
            }
            // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
            Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
                startActivity(this)
            }
            return true
        }
    }
    

Java

    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if ("www.example.com".equals(Uri.parse(url).getHost())) {
                // This is my website, so do not override; let my WebView load the page
                return false;
            }
            // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(intent);
            return true;
        }
    }
    

Luego, crea una instancia de este nuevo WebViewClient para la WebView:

Kotlin

    val myWebView: WebView = findViewById(R.id.webview)
    myWebView.webViewClient = MyWebViewClient()
    

Java

    WebView myWebView = (WebView) findViewById(R.id.webview);
    myWebView.setWebViewClient(new MyWebViewClient());
    

Ahora, cuando el usuario hace clic en un vínculo, el sistema llama a shouldOverrideUrlLoading(), que comprueba si el host de la URL coincide con un dominio específico (como se definió más arriba). Si coincide, el método muestra el valor "false" para no anular la carga de la URL (permite que la WebView cargue la URL como de costumbre). Si el host de la URL no coincide, se crea un Intent a fin de iniciar la actividad predeterminada para manejar las URL (que se resuelve en el navegador web predeterminado del usuario).

Cuando tu WebView anula la carga de URL, acumula automáticamente un historial de las páginas web visitadas. Puedes navegar hacia atrás y hacia adelante por el historial con goBack() y goForward().

Por ejemplo, a continuación, se muestra cómo tu Activity puede usar el botón Atrás del dispositivo para navegar hacia atrás:

Kotlin

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        // Check if the key event was the Back button and if there's history
        if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
            myWebView.goBack()
            return true
        }
        // If it wasn't the Back key or there's no web page history, bubble up to the default
        // system behavior (probably exit the activity)
        return super.onKeyDown(keyCode, event)
    }
    

Java

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // Check if the key event was the Back button and if there's history
        if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
            myWebView.goBack();
            return true;
        }
        // If it wasn't the Back key or there's no web page history, bubble up to the default
        // system behavior (probably exit the activity)
        return super.onKeyDown(keyCode, event);
    }
    }

El método canGoBack() muestra el valor "true" si hay un historial de páginas web que el usuario puede visitar. Del mismo modo, puedes usar canGoForward() para verificar si hay un historial de páginas siguientes. Si no realizas esta verificación, cuando el usuario llega al final del historial, goBack() o goForward() no llevan a cabo ninguna acción.

Cómo manejar los cambios de configuración del dispositivo

Durante el tiempo de ejecución, se producen cambios en el estado de la actividad cuando se modifica la configuración de un dispositivo, por ejemplo si los usuarios giran el dispositivo o descartan un editor de método de entrada (IME). Estos cambios causarán que se destruya la actividad de un objeto WebView y se cree una actividad nueva, lo que también generará un nuevo objeto WebView que cargará la URL del objeto destruido. Para modificar el comportamiento predeterminado de tu actividad, puedes cambiar la forma en que maneja los cambios de orientation en tu manifiesto. Para obtener más información sobre cómo manejar los cambios de configuración durante el tiempo de ejecución, consulta el artículo Cómo administrar los cambios de configuración.

Cómo administrar las ventanas

De forma predeterminada, las solicitudes para abrir ventanas nuevas se ignoran. Esto es así tanto si las abre JavaScript como si las abre el atributo de destino en un vínculo. Puedes personalizar el WebChromeClient para aplicar tu propio comportamiento cuando se abran varias ventanas.

Precaución: Para proteger mejor tu app, es recomendable evitar que se abran ventanas emergentes y nuevas. La forma más conveniente de implementar este comportamiento es pasar "true" en setSupportMultipleWindows(), pero no anular el método onCreateWindow() del cual depende setSupportMultipleWindows(). Sin embargo, ten en cuenta que esta lógica también evita que se cargue cualquier página que use target="_blank" en sus vínculos.