Desarrollo de aplicaciones mixtas (web/nativa) en Android.
Introducción
En ocasiones crear una aplicación basada en una arquitectura mixta entre una aplicación nativa y una aplicación web y comunicarlas en base a nuestras necesidades puede ser muy adecuado y ahorrarnos mucho tiempo de desarrollo.
Todos estaremos deacuerdo en que:
- Una aplicación web (html, javascript, css, etc) tiene la ventaja de que no hay que distribuir la aplicación cuando hay un cambio en la misma, con cambiar el código en el servidor bastaría.
- Una aplicación web no puede acceder directamente a los recursos del dispositivo: GPS, camara de fotos, agenda, etc.
Y digo yo, ¿por qué no una mezcla que aproveche ambas ventajas?.. pues bien, de eso se trata este pequeño tutorial.. ver como comunicarnos entre ambas partes web y nativa
Si quieres trastear, puedes descargarte el código fuente desde clic aquí. Si quieres probarlo directamente
en tu dispositivo puedes descargarte la aplicación desde clic aquí
Construcción de una aplicación mixta en Android
El código fuente está autocomentado, no creo que tengas problemas si tienes una base de programación en Android.
Captura de pantalla de la aplicación a construir:
assert/carlos-garcia.html
única página web que compone la aplicación, aunque podría haber sido generada dinámicamente en un servidor, en este ejemplo está ubicada como un estático en la carpeta assert
del proyecto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<html> <head> <title>Android WebView y NativeApp</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <script language="javascript"> function callFromAndroidToJS(texto) { document.getElementById("target").innerHTML=texto; } function callFromJSToAndroid() { jsNativeInterface.metodoDemo1(); } function callFromJSToAndroidForReadJson() { var datosJson = eval('(' + jsNativeInterface.metodoDemo2() + ')'); document.getElementById("target").innerHTML=datosJson.timestamp; } </script> <style type="text/css"> a { color: green; text-decoration: underline;} </style> </head> <body> <h1>carlos-garcia.es</h1> <ul> <li><p>Prueba 1: <a onclick="callFromJSToAndroid();">Haga clic aquí</a> para invocar desde el JavaScript a la aplicación Android</p></li> <li><p>Prueba 2: <a onclick="callFromJSToAndroidForReadJson();">Haga clic aquí</a> para invocar desde el JavaScript a la aplicación Android y leer JSON</p></li> <li><p>Prueba 3: <span id="target">Este texto será sustituido desde la aplicación nativa</span></p></li> </ul> </body> </html> |
Ten mucha precaución con la parte de javascript, pues si tienes un error como por ejemplo poner «var» en los parámetros de los métodos no ves errores en los los, simplemente NO funciona la comunicación.
res/layout/main.xml
Interface gráfico de la aplicación, observe que abajo hay un WebView que será donde se muestre la parte web.
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/txt1" android:hint="Escribe algo y acepta" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:id="@+id/btn1" android:text="Aceptar" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <WebView android:id="@+id/webview" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </LinearLayout> |
es.carlosgarcia.android.MyAndroidToJsInterface
Interface de comunicación entre la parte web y la parte no web (nativa).
1 2 3 4 5 6 7 8 9 10 11 |
package es.carlosgarcia.android; /** * Pequeño ejemplo de aplicación mixta: Web y Nativa. Invocar desde JavaScript a la parte Nativa y viceversa. * @author Carlos García Pérez. * @see http://carlos-garcia.es */ public interface MyAndroidToJsInterface { public void metodoDemo1(); public String metodoDemo2(); } |
es.carlosgarcia.android.WebDemoActivity
Actividad de la aplicación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package es.carlosgarcia.android; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.webkit.WebView; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; /** * Pequeño ejemplo de aplicación mixta: Web y Nativa. Invocar desde JavaScript a la parte Nativa y viceversa. * @author Carlos García Pérez. * @see http://carlos-garcia.es */ public class WebDemoActivity extends Activity implements MyAndroidToJsInterface, OnClickListener { private WebView browser; private EditText txt1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.main); browser = (WebView) findViewById(R.id.webview); Button btn1 = (Button) findViewById(R.id.btn1); txt1 = (EditText) findViewById(R.id.txt1); browser.getSettings().setJavaScriptEnabled(true); browser.addJavascriptInterface(this, "jsNativeInterface"); browser.loadUrl("file:///android_asset/carlos-garcia.html"); btn1.setOnClickListener(this); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && browser.canGoBack()) { browser.goBack(); return true; } return super.onKeyDown(keyCode, event); } /** * Llamamos desde Android a Javascript * android.view.View.OnClickListener */ public void onClick(View v) { browser.loadUrl("javascript:callFromAndroidToJS('" + txt1.getText().toString() + "')"); } /** * Este método es invocado desde JavaScript => Muestra un mensaje por pantalla * MyAndroidToJsInterface */ public void metodoDemo1() { Toast.makeText(this, "Invocado el metodo: metodoDemo1", Toast.LENGTH_SHORT).show(); } /** * Este método es invocado desde JavaScript => Devuelve un objeto JSON desde la aplicación Nativa * MyAndroidToJsInterface */ public String metodoDemo2() { String toReturn = null; try { JSONObject json = new JSONObject(); json.put("timestamp", System.currentTimeMillis()); json.put("autor", "http://www.carlos-garcia.es"); toReturn = json.toString(); } catch (JSONException e) { // no se dará } return toReturn; } } |
AndroidManifest.xml
Archivo de configuración de la aplicación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="UTF-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="es.carlosgarcia.android" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true"> <activity android:name=".WebDemoActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="3" /> <uses-permission android:name="android.permission.INTERNET"/> </manifest> |
Conclusiones
Como veis no es dificil comunicar ambas partes y aprovechar las ventajas de cada paradigma, ahorrándonos mucho tiempo y coste.
Espero que os haya sido útil, un saludo.
Carlos García.
Hola el tutorial me parece bueno , quería hacerte una consulta, si la pagina html la tuvieramos en un apache podriamos comunicarnos igualmente con la Activity ??. muchas gracias.
Y si quisiera desde
public void metodoDemo1()
{
Toast.makeText(this, \\\»Invocado el metodo: metodoDemo1\\\», Toast.LENGTH_SHORT).show();
}
En lugar del TOAST, modificar el valor de un EditText que previamente he declarado como public en la clase y asociado en onCreate…..
¿por qué no funciona?
Tiene algo que ver con View.???
Saludos una pregunta si tengo una aplicacion web con mapas q tengo q hacer para q aparezcan mapas, marcadores, rutas
Cual fuel el método que usaste para convertir el Código fuente en apk?