====== Android ======
===== General =====
==== Instalación ====
Para desarrollar una aplicación para android necesitaremos tener:
- Eclipse
- El SDK de Android
- Instalar el plugin para eclipse de desarrollo de Android.
La web oficial de desarrollo en Android es [[http://android-developers.blogspot.com/index.html]].
==== Arquitectura ====
La arquitectura de Android está estructurada por capas y cada capa utiliza los servicios de las que tiene por debajo. Las capas empezando por la más baja son:
* **El kernel de Linux**, la capa que permite la comunicación con el hardware.
* **Librerías nativas**, escritas en C\C++ y compiladas para cada arquitectura de cada teléfono. Entre ellas:
* El //surface manager//, el administrador gráfico que gestiona el entorno, las ventanas y las aplicaciones. Interactuar con él es más sencillo que diréctamente con el hardware.
* Las librerías de //gráficos 2d y 3d//.
* //Códecs// multimedia, así como AAC, MP3, MPEG4...
* //SQLite//, que gestiona las bases de datos internas.
* El motor html (//webkit//), para mostrar archivos de páginas web.
* **El motor de Android**, compuesto básicamente por la //Dalvik VM//, esta es una versión de Java optimizada para móviles diseñada por Google. Esta corre archivos .dex en vez de .jar, pero a su vez también hace uso de algunas librerías de Java. Una comparación con Java es que:
* Utiliza la sintaxis de Java5.
* El uso de ''doubles'' o ''floats'' es emulado por software.
* El uso de threads también es limitado, no se deberían de utilizar más de 2.
* Mejor que utilizar el ''finalize'' es utilizar los métodos ''close'' o ''terminate''.
* De las librerías de java hay algunas que estan soportadas (''java.awt.font'', ''java.io'', ''java.lang'', ''java.math''...) y otras que no (java.applet, ''java.beans'', ''javax.print'', ''javax.sound''...).
* Otras librerías de terceros también son soportadas: ''org.apache.http'', ''orj.json'', ''org.xml.sax'', ''org.xmlpull.v1''.
* **El framework de aplicaciones**, utilizado para programar para Android dándonos una serie de bloques de código para utilizar en nuestro desarrollo. Entre ellos encontramos:
* El //activity manager//, que controla el ciclo de vida de las aplicaciones.
* //Proveedores de contenido//, código para acceder a los datos contenidos en el teléfono (por ejemplo los contactos).
* El //resource manager//, que gestiona los elementos de un programa que no son código (imágenes, audio...).
* El //location manager//, para conocer la ubicación del teléfono.
* El //notification manager//, encargado de mostrar alertas y al usuario.
* Las **aplicaciones**, la capa que realmente ve el usuario.
{{ fw:android:android_architecture.png?450 |}}
==== El ciclo de vida de una aplicación ====
Android trabaja únicamente con una aplicación a la vez (que no proceso), por ejemplo, al iniciar el sistema se inicia la aplicación Home (con menús, reloj, mensajes para el usuario...); cuando el usuario inicia otra aplicación la Home queda en segundo plano y, por lo tanto, recibe menos "atención". A medida que se van iniciando aplicaciones estas son guardadas en la pila de aplicaciones (//application stack//) por el //activity manager//. El usuario podrá volver a la aplicación anterior como si de una navegación web se tratase. \\ \\
Cada entorno (ventana, aplicación...) está representada por una clase ''Activity''. Una aplicación realmente es una (o más) ''Activity'' enlazada a un proceso Linux, pero la aplicación no queda finalizada aunque el proceso Linux lo esté. \\ \\
Las activities permanecen en un estado controlado por el sistema, no por el programador. Cuando el estado cambia avisa a la activity mediante eventos pudiendo ser las transiciones de la siguiente forma:
{{ fw:android:android_activity_lifecycle.png?450 |}}
Es decir, se han de implementar los siguientes métodos y el sistema los llamará en el momento adecuado:
* ''onCreate'', cuando la activity inicia, sólo se llama una vez. Método adecuado para crear la interficie de usuario.
* ''onStart'', indica que la activity va a mostrarse al usuario.
* ''onResume'', indica que el usuario ya puede interactuar con la activity. Método adecuado para iniciar sonidos o animaciones.
* ''onPause'', se inicia cuando la actividad está apunto de ir a un segundo plano. Aquí se debería guardar el estado interno actual de la aplicación.
* ''onStop'', cuando la actividad va a finalizarse (si el sistema tiene suficiente memoria puede no ser llamado nunca).
* ''onRestart'', cuando la actividad vuelve a un primer plano.
* ''onDestroy'', cuando la actividad va a eliminarse.
* ''onSaveInstanceState'' y ''onRestoreInstanceState'' corresponden a métodos para guardar el estado de la interficie de usuario (lo que se está escribiendo en un cuadro de texto y tal), pero en las últimas versiones no es necesario debido a que ya lo hace el sistema por sí solo.
Las aplicaciones deberían guardar su estado en el método ''onPause'' debido a que no se sabe si continuarán en ejecución en un futuro.
==== Bloques de desarrollo ====
Existen distintos tipos de bloques que el usuario puede aprovechar (heredando de ellos) en su código:
* //Activities//, es una pantalla de usuario.
* //Intents//, son algo así como eventos, describen una acción (enviar un mail, hacer una llamada...). Por ejemplo el de enviar un mail, si la aplicación va a enviar un mail llamaría a este intent y el programa receptor arrancaría para enviar dicho mail; al contrario, por ejemplo, sería indicar ser el receptor de este intent, de esa forma cuando una aplicación fuese a enviar un mail avisaría a la tuya.
* //Servicios//, programas que se están ejecutando siempre y en segundo plano. Por ejemplo el reproductor de música, se inicia como una Activity pero si cambias de aplicación pasa a ser un servicio que sigue reproduciendo la música.
* //Content Providers//, son algo así como bibliotecas\librerías, que engloban código que se puede compartir entre aplicaciones.
==== Permisos ====
En el fichero llamado ''Android-Manifest.xml'' se indica a qué partes del sistema accederá la actividad y será el ''Package Manager'' quien dará esos permisos a partir de la aceptación del usuario.
* ''INTERNET'': Acceso a internet.
* ''READ_CONTACTS'': Leer pero no escribir en los contactos del usuario.
* ''WRITE_CONTACTS'': Escribir pero no leer los contactos del usuario.
* ''RECEIVE_SMS'': Monitorear mensajes de texto que lleguen.
* ''ACCESS_COARSE_LOCATION'': Saber la localización simple (a partir de antenas o wifi).
* ''ACCESS_FINE_LOCATION'': Conocer la posición más concreta, a partir del GPS.
Un ejemplo del código en el ''Android-Manifest.xml'' es el siguiente que da la posivilidad de acceder a los SMS:
===== Interfaces de usuario =====
Cuando creamos un proyecto con Eclipse mediante la SDK de Android se generan varios directorios, entre ellos el ''res'' que contiene los recursos. Estos pueden ser archivos xml con textos que se utilicen en la aplicación, o xml que representen layouts (interficie), o sonidos, gráficos, iconos... Cuando se compila el proyecto aparece otro directorio, ''gen'', el cual contiene la clase generada ''R'' que corresponde a los recursos y que, a su vez, contiene los identificadores (variables int estáticas) de los recursos creados. \\ \\
Cuando utilizamos archivos xml como recursos de un proyecto Android estos han de comenzar con la siguiente declaración:
==== Layouts ====
Los layouts son contenederes para objetos hijo, su función es posicionar estos objetos en la pantalla. Los más comunes son:
* ''FrameLayout'': Coloca los hijos a partir de la posición //top-left//.
* ''LinearLayout'': Coloca los hijos en una columna o fila.
* ''RelativeLayout'': Coloca los hijos en relación a las características de otros o del padre.
* ''TableLayout'': Organiza los elementos en columnas-filas.
Los parámetros comunes para ellos:
* ''xmlns:android="http://schemas.android.com/apk/res/android"'': Debería estar definida en la primera tag del XML, define el namespace del XML para Android.
* ''android:layout_width=""'' y ''android:layout_height=""'': Para indicar cómo se expandirá. Sus posibles valores son: ''fill_parent'' y ''wrap_content''.
Para indicar un layout especial para el momento en el que se gire el teléfono crearemos una nueva carpeta, la ''layout-land'', y en ella insertaremos los xml correspondientes, por ejemplo el ''main.xml''.
=== Otros contenedores ===
Pero los layouts no son los únicos contenedores (ni tampoco los primeros que se han de poner en una aplicación). Por ejemplo, si queremos que un entorno tenga scroll añadiremos:
Es decir...
* ''ScrollView'': Para entornos con scroll.
* ''WebView'': Si queremos que sea código html.
==== Controles dentro del layout ====
En una layout podemos añadir elementos, estos serán tags xml y se agregarán de la siguiente:
Para acceder a un control mediante código necesitamos indicar un identificador, para ello utilizamos la propiedad ''android:id="@+id/identificador"''. Esto hará que en la clase ''R'' se cree un identificador para ese elemento.
=== Propiedades comunes ===
* ''android:text'': Es el texto que aparece en el control. En un control podemos indicar su valor a partir de los recursos. Por ejemplo si queremos acceder a un valor dentro del ''strings.xml'' pondremos como su valor algo así como ''@string/nombre_variable''. Pero también podremos poner el texto directamente lo.
* ''android:gravity'': Es la posición de los elementos internos: ''center''.
* ''android:textColor'': Color del texto.
=== Lista de controles ===
* ''TextView'': Corresponde a un texto simple (algo así como una label) dentro de la layout.
* ''Button'': Corresponde a un botón.
* ''ImageButton'': Botón con imágen. La imágen ha de estar en ''res/drawable-xxx'' y para indicar cual será la que se mostrará lo haremos a partir de la propiedad ''src'', indicando ''@drawable/nombre-de-archivo-sin-extensión''. Por ejemplo: ''android:src="@drawable/state"''.
==== Recursos ====
Un recurso es la parte de la aplicación que no es código (por ejemplo imagenes, sonidos, textos...) pero que se compilan con esta. Han de guardarse en el directorio ''res'' de la aplicación. Cuando lo hacen se crea una clase denominada ''R'' que contiene los identificadores a cada uno de los recursos para poder acceder a estos desde el código, para ello en el directorio ''res'' se crean subdirectorios, como por ejemplo el ''layout'' y dentro de este archivos como por ejemplo el ''main.xml'', que sería el layout principal. Para acceder a este layout haríamos ''R.layout.main''. La clase ''R'' la maneja automáticamente Eclipse.
=== Strings ===
Dentro del proyecto, en la carpeta ''res'' encontraremos la subcarpeta ''values'' con el fichero ''string.xml''. Este corresponde a los textos de la aplicación.
Hello World, Test!TestApagarEncender
Podemos añadir código en formato html y con multilinia colocando el carácter ''\'' al inicio:
\
Un recurso es la parte de la aplicación que no es código (por ejemplo imagenes, sonidos, textos…)
pero que se compilan con esta. Han de guardarse en el directorio res de la aplicación. Cuando lo
hacen se crea una clase denominada R que contiene los identificadores a cada uno de los recursos
para poder acceder a estos desde el código, para ello en el directorio res se crean subdirectorios,
como por ejemplo el layout y dentro de este archivos como por ejemplo el main.xml, que sería el layout
principal. Para acceder a este layout haríamos R.layout.main. La clase R la maneja automáticamente Eclipse.
=== Otros ===
Por ejemplo colores, agregamos el fichero ''res\values\colors.xml'' y le añadimos el siguiente contenido:
#BA030E#1D7900#EDF5FF#000000
Ahora en las propiedades que acepten colores podremos asignar estos valores como ''@color/black'', por ejemplo.
=== Más... ===
* Para acceder a los recursos desde una ''Activity'' utilizaremos el método ''getResources()''.
String[] countries = getResources().getStringArray(R.array.countries_array);
==== Activity ====
Las activities son lo equivalentes a "ventanas" dentro de nuestra aplicación. \\
Para agregar una Activity a nuestra aplicación necesitamos:
- Una layout. Agregariamos el archivo .xml en ''/res/layout/'' correspondiente.
- Una clase java que herede de ''android.app.Activity''.
- Agregarla al ''AndroidManifest.xml''.
- Lanzar la Activity a partir de un ''Intent''.
=== Clase Activity ===
La siguiente sería una clase que hereda de ''Activity'':
package my.test;
import android.app.Activity;
import android.os.Bundle;
public class About extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.about);
}
}
Para agregarla al ''AndroidManifest.xml'' abriremos el archivo y añadiremos la siguiente línea dentro de la tag ''application'':
Siendo ''About'' el nombre de la clase y ''State'' el título que queramos darle. \\
Cuando una clase Activity llamaremos al ''setContentView'' para indicar su layout.
package my.test;
import android.app.Activity;
import android.os.Bundle;
public class Test extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
=== Abriendo Activities ===
Para iniciar una ''Activity'' necesitaremos utilizar un objeto ''Intent'' y lo haremos a partir de los siguientes métodos:
* ''startActivity'': Inicia una nueva actividad, esta reemplazará la actual en la pila de actividades.
* ''startActivityForResult'': Cuando queramos recoger información de la ''Activity'' que iniciamos, para recoger su resultado necesitaremos sobreescribir el método ''onActivityResult'' de la ''Activity'' que ejecuta este. Para controlar que se ha escogido un resultado y que no se ha salido con la tecla de //atrás// podremos recoger en el primer integer el estado, si este es ''RESULT_OK'' es que ha salido bien.
Para pasar argumentos entre objetos ''Activity'' utilizaremos la clase ''android.os.Bundle'', en esta podemos agregar información (mediante el método '''') y recogerla (mediante ''''). \\
Por ejemplo una Activity A va a abrir una Activity B, para enviarle información a esta haremos:
Bundle b = new Bundle();
String strValue = "Test";
b.putString("elements", strValue);
b.putInt("type", Main.AskClient);
Intent i = new Intent(this, OpenVideo.class);
i.putExtras(b);
this.startActivityForResult(i, 0);
Al crearse la Activity B lo primero que hace es mirar los datos pasados:
this.type = this.getIntent().getExtras().getInt("type");
Una vez que ha hecho todo lo que tiene que hacer devuelve la información en otro ''Bundle'':
Bundle bundle = new Bundle();
bundle.putInt("selected", arg2 - 1);
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
this.finish();
Y en el ''onActivityResult'' de la Activity A:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK)
return;
Bundle bundle = data.getExtras();
int type = bundle.getInt("type");
=== Métodos y propiedades de las Activity ===
* Para finalizar una ''Activity'' llamaremos a su método ''finish()''.
==== Intent ====
Los ''android.content.Intent'' son los que lanzan objetos ''Activity''. Desde un objeto ''Activity'' crearemos un objeto ''Intent'', en su constructor indicaremos el objeto que lo crea (''this'') y el objeto ''Class'' correspondiente a la clase ''Activity'' que queramos lanzar.
Intent i = new Intent(this, About.class);
this.startActivity(i);
==== Vistas ====
=== ListView ===
Muestran en una ''Activity'' una lista de elementos. Existe la ''Activity'' especial para mostrar estas llamada ''ListActivity'', por lo tanto si queremos crear una lo que haremos será agregar una clase que herede de esta. Para rellenarla necesitaremos utilizar objetos ''ListAdapter'' que corresponden a listas\arrays que le agregaremos, para recoger cuando se ha hecho click sobre un elemento utilizaremos el método ''setOnItemClickListener'' de la lista interna que recogeremos mediante ''getListView()'':
public class OpenVideo extends ListActivity implements OnItemClickListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String param = this.getIntent().getExtras().getString("elements");
String[] elems = param.split("\\?");
setListAdapter(new ArrayAdapter(this, R.layout.openvideo, elems));
android.widget.ListView lv = this.getListView();
lv.setOnItemClickListener(this);
}
public void onItemClick(AdapterView> arg0, View arg1, int arg2, long arg3) {
Bundle bundle = new Bundle();
bundle.putInt("selected", arg2);
setResult(RESULT_OK, mIntent);
this.finish();
}
}
Por lo demás, al ser una Activity tendremos que agregarla al ''AndroidManifest.xml'' y crear su layout:
==== Interactuar mediante Java ====
=== Acceder a los elementos ===
Para acceder a los elementos desde un método de una clase ''Activity'' utilizaremos el ''findViewById()'' pasándole por parámetro el id asignado (mediante la clase ''R''), nos devolverá un objeto ''View'':
View continueButton = this.findViewById(R.id.continue_button);
continueButton.setOnClickListener(this);
=== Capturar eventos ===
Para capturar el click en un botón, por ejemplo, debemos asignar un objeto ''android.view.View.OnClickListener.'' al método ''setOnClickListener''.
View btn = this.findViewById(R.id.btnState);
btn.setOnClickListener(this);
La clase donde se haga esto, evidentemente deberá implementar la interfaz ''OnClickListener''. \\
Luego, el en método ''onClick'':
@Override
public void onClick(View arg0) {
switch (arg0.getId())
{
case R.id.btnState:
android.content.Intent i = new Intent(this, About.class);
startActivity(i);
break;
}
}
==== Como... (en controles) ====
=== ... Funcionan los tamaños ===
En Android existen varios tipos de medidas:
* ''px'': Píxels, puntos en la pantalla.
* ''in'': Inches.
* ''mm'': Milimetros.
* ''pt'': Points, 1/72 de una //inch//.
* ''dp'': Density-Independent Pixels. ¿?
* ''dip'', sinónimo de dp.
* ''sp'': Scale-independent pixels. Como los dp pero escalados según la fuente configurada por el usuario.
Es aconsejable utilizar ''sp'' para los textos y ''dip'' para todo lo demás.
=== ... Crear mensajes ===
* **android.widget.Toast**
Son los más sencillos, muestran un mensaje en la parte de abajo de la pantalla:
Toast.makeText(this.getApplicationContext(), "Recuerda inicializar la wifi!!", Toast.LENGTH_LONG).show();
* **android.app.AlertDialog**
===== Programando aplicaciones =====
==== Temas ====
Los temas son como los CSS pero aplicados a los controles. Podemos definir los nuestros (en ''res/values/styles.xml''), pero ya existen predefinidos. \\
Para indicar un tema para un objeto ''Activity'' en su tag correspondiente del ''AndroidManifest.xml'' agregaremos ''android:theme=""''. \\
Si vamos a agregar un estilo predefinido haremos el valor será: ''@android:style/estilo''.
=== Temas predefinidos ===
* ''Theme.Dialog''.
==== Menús ====
Android permite mostrar dos tipos de menús, el primero es el que aparece al pulsar sobre la tecla ''Menu'' (menú de opciones), el segundo tras dejar apretado el dedo sobre la pantalla.
=== Menú de opciones ===
Este menú lo guardaremos en el fichero ''res/menu/menu.xml''. Y será del siguiente estilo:
Para hacerlo aparecer necesitaremos sobreescribir el método ''onCreateOptionsMenu'' de la ''Activity'', este se llama cada vez que se pulsa la tecla física ''Menu''. Para llamarlo utilizaremos un objeto la clase ''MenuInflater'' (recogeremos el objeto de esta mediante el método ''getMenuInflater'') llamando al método ''inflate'':
@Override
public boolean onCreateOptionsMenu(Menu menu) {
android.view.MenuInflater inflater = this.getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
Para detectar qué elemento se ha pulsado sobreescribiremos el ''onOptionsItemSelected'':
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.item01:
break;
}
return super.onOptionsItemSelected(item);
}
==== Preferencias ====
Las preferencias realmente son clases ''android.preference.PreferenceActivity'' y estas, como su nombre indica, heredan de ''Activity'', utilizan un //layout// diferente a otras ''Activity''. Como tal, para abrir unas preferencias necesitaremos un ''Intent'':
startActivity(new Intent(this, Settings.class));
Este abre la clase Settings, que hereda de ''Activity'' que a su vez se enlaza al xml mediante el método ''addPreferencesFromResource'':
package my.test;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class Settings extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
}
}
Y a la vez también necesitamos agregarla en el ''AndroidManifest.xml'':
Lo que cambia más en cuanto a una ''Activity'' común es el fichero xml que colocaremos en ''res/xml/settings.xml'':
En él podemos utilizar elementos que luego podrán ser preguntados a partir de su propiedad ''key'', en el antiguo caso sólo hay un ''CheckBox'' y su key es //music//. \\
Los elementos que podemos encontrar son:
* ''CheckBoxPreference''
* ''EditTextPreference''
Para leer los valores lo haremos utilizando el ''android.preference.PreferenceManager'' de la siguiente forma:
boolean b = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean("music", true);
Al método ''getBoolean'' se le pasa el key de una setting boolean y, si no estubiese definida, su valor por defecto.
==== Estados de una aplicación ====
==== Guardar\Cargar datos ====
==== Log ====
El SDK de Android agrega un visor de mensajes de log del teléfono (Window -> Show View -> Other -> Android -> LogCat). Este muestra los errores de las aplicaciones, pero además podemos escribir en él mediante la clase ''Log'':
* ''Log.(e)'': Errores.
* ''Log.(w)'': Warnings.
* ''Log.(i)'': Informacion.
* ''Log.(d)'': Debuggin.
* ''Log.(v)'': Verbose.
==== Como... ====
=== Minis ===
===== Notas =====
* Podemos utilizar una clase anidada para recoger los eventos, pero por cada clase anidada que creemos gastaremos 1KB extra de memoria.
* Para poder depurar un programa Android desde Eclipse, a parte de indicarlo al ejecutar (''Debug As -> Android Application''), debemos añadir en ''AndroidManifest.xml'' la siguiente propiedad a la tag ''application'': ''android:debuggable="true"''.
* El SDK para Android viene con herramientas muy útiles para hacer un seguimiento de los threads de los procesos, controlar el emulador (hacer llamadas, enviar mensajes...), ver la memoria gastada, copiar y pegar ficheros en este...
==== El emulador ====
* ''ctrl + f11'' harán que el emulador cambie de horizontal a vertical.
==== En un dispositivo físico ====
Para poder debugar desde el teléfono necesitaremos instalar los drivers.
* El teléfono tiene que tener activada la depuración:
- //Ajustes -> Aplicaciones -> Configuración -> Permitir orígenes desconocidos//
- //Ajustes -> Aplicaciones -> Configuración -> Desarrollo -> Depuración USB//
=== Drivers desde Ubuntu ===
Creamos el siguiente archivo.
nano /etc/udev/rules.d/51-android.rules
Escribimos lo siguiente.
SUBSYSTEM=="usb_device", SYSFS{idVendor}=="0bb4", MODE="0666"
Y el siguiente comando para activar el driver:
chmod a+r /etc/udev/rules.d/51-android.rules
Aunque en otros he visto: ''chmod a+rx /etc/udev/rules.d/51-android.rules''
=== Herramientas ===
* El comando ''ddms'' del directorio tools nos abrirá el monitor de debug de Dalvik, con él podremos hacer capturas de pantalla.