En este video te explicamos en detalle todo el proceso para publicar una Aplicación Android…
Proyecto conexión Android con MySQL: versión Android Studio – Android 5.0
5.
Proyecto conexión Android con MySQL: versión Android Studio – Android 5.0
En este tutorial vamos a desarrollar una versión actualizada para Android 5.0 (API 21) del proyecto de conexión de una Aplicación Android con una base de datos externa MySQL. Esto nos exigirá utilizar la clase AsyncTask. , que será la encargada de procesar en segundo plano la conexión a dicha base de datos. Otra diferencia con la versión anterior de este proyecto es que usaremos el entorno de desarrollo Android Studio en vez de Eclipse.
Recordamos que en el proyecto creábamos una conexión a la base de datos MySQL, para posteriormente realizar consultas Transact-SQL mostrando el resultado de dichas consultas.
Al final de este tutorial, puedes descargar todo el código del proyecto.
Elementos del proyecto
El proyecto se centrará en el proceso de conexión a la base de datos, y explicaremos cómo ejecutar consultas desde un nuevo hilo de la aplicación a través de un componente EditText. Vamos a enumerar los elementos que intervendrán en dicho proceso:
- Clase MainActivity, que hereda de la clase base Activity, dónde se define la lógica de conexión a la base de datos MySQL y envío de los datos de la conexión a la siguiente Activity.
- Clases ConexionAsincrona y ConsultasAsincrona, que heredan de la clase base AsyncTask, encargadas de procesar en segundo plano la conexión con la base de datos externa y realizar la consulta proporcionada por el usuario.
- Clase ConsultasSQL, que hereda de la clase base Activity, encargada de controlar la lógica de inserción de una consulta Transact-SQL, mostrando el resultado de dicha consulta.
- Librería ‘mysql-connector-java-5.1.31-bin.jar’ para implementar las funcionalidades asociadas a la lectura, escritura o eliminación de una base de datos MySQL.
Estructura del Proyecto
Vemos la estructura del proyecto creado en esta ocasión con Android Studio:
Código fuente del Proyecto
Vamos a detallar las diferentes clases, layouts y consultas SQL que utilizamos en este proyecto.
ConsultasMySQL\app\src\main\java\com\academiaandroid\consultasmysql\MainActivity.java
- Se define una clase llamada MainActivity, que hereda de la clase base Activity, encargada de recoger los datos de conexión al servidor MySQL:
1public class MainActivity extends Activity { - Se declaran los componentes necesarios para introducir los datos de conexión al servidor:
12private EditText edServidor, edPuerto, edUsuario, edPassword;private String baseDatos = "Tienda"; - Se enlazan dichos componentes con sus recursos a nivel de layout:
12345edServidor = (EditText)findViewById(R.id.edServidor);edPuerto = (EditText)findViewById(R.id.edPuerto);edUsuario = (EditText)findViewById(R.id.edUsuario);edPassword = (EditText)findViewById(R.id.edPassword); - Se define un método llamado
conectarMySQL() , que devolverá un valor de true o false, a partir del evento de conexión realizado por el usuario, dónde se asignará el driver a una variable de tipo String, se declara un Array de Strings, dónde se almacenan los datos para establecer la conexión al servidor MySQL, y dónde finalmente se invoca al método
execute() del hilo secundario pasándole como parámetros los datos de conexión y cita a almacenar en la tabla:
12345678910111213141516171819202122232425262728public boolean conectarMySQL(){boolean estadoConexion = false;[...]String driver = "com.mysql.jdbc.Driver";try{Class.forName(driver).newInstance();if(user.equals("") || password.equals("") || puerto.equals("") || ip.equals("")){Toast.makeText(MainActivity.this,"Debe indicar todos los datos solicitados.", Toast.LENGTH_LONG).show();}else{String[] datos = new String[]{ip,puerto,baseDatos,user,password};estadoConexion = new ConexionAsincrona().execute(datos).get();}}catch(Exception ex){Toast.makeText(MainActivity.this,"Error al comprobar las credenciales: " + ex.getMessage(), Toast.LENGTH_LONG).show();}return estadoConexion;}
ConsultasMySQL\app\src\main\java\com\academiaandroid\consultasmysql\ConexionAsincrona.java
- Clase ConexionAsincrona que hereda de la clase base AsyncTask, que permite comunicarse con el hilo principal, y que realizará tareas en segundo plano. Esta clase define tres tipos genéricos:
1public class TareaAsyncTask extends AsyncTask<params, progress, result>
* Progress: parámetro para actualizar el hilo principal o UIThread
* Result: es el resultado devuelto por el procesamiento en segundo plano:
12public class ConexionAsincrona extends AsyncTask<String, Void, Boolean> { - Se declaran una variable de tipo
Connection
:
12private Connection conexionMySQL; - Se sobrescribe el método
doInBackground , que será donde se defina el código que se ejecutará en segundo plano. Recibe como parámetro un array de strings declarados en la clase MainActivity.java, al llamar al método
execute(Params) . Inicialmente se establece la conexión con el Servidor MySQL indicándole la cadena de conexión formada por la dirección ip, puerto del servidor, la base de datos a la que vamos a conectarnos, y el usuario y contraseña de acceso al servidor:
12345678910111213@Overrideprotected Boolean doInBackground(String... datos){String SERVIDOR = datos[0];String PUERTO = datos[1];String BD = datos[2];String USUARIO = datos[3];String PASSWORD = datos[4];boolean estadoConexion = false;try{conexionMySQL = DriverManager.getConnection("jdbc:mysql://" + SERVIDOR + ":" + PUERTO + "/" + BD,USUARIO,PASSWORD); - A continuación, se comprueba que la conexión al servidor se ha establecido para cambiar el valor de la variable
estadoConexion
a true:
123456789if(!conexionMySQL.isClosed()){estadoConexion = true;}}catch(SQLException ex){Log.d("No ha sido posible conectar con la base de datos", ex.getMessage());} - Finalmente se cierran las conexiones abiertas con el servidor y se devuelve el estado de la conexión:
1234567891011121314finally{try{conexionMySQL.close();} catch (SQLException e){e.printStackTrace();}}return estadoConexion;}}
ConsultasMySQL\app\src\main\java\com\academiaandroid\consultasmysql\ConsultasSQL.java
- Se define una clase llamada ConsultasSQL, que hereda de la clase base Activity, encargada de permitir al usuario realizar consultas Transact-SQL sobre la base de datos «Tienda»:
12public class ConsultasSQL extends Activity { - A continuación, se declaran los componentes y clases necesarias para realizar consultas a una base de datos en MySQL:
12345private TextView txtBaseDatos,txtPuerto,txtServidor,txtUsuario,txtPass;private EditText edConsulta, edResultado;private Bundle bundle;private String[] datosConexion = null; - Posteriormente, dentro del método
onCreate() , se enlazan los componentes con los recursos definidos en el layout:
123456789txtServidor = (TextView)findViewById(R.id.txtServidor);txtPuerto = (TextView)findViewById(R.id.txtPuerto);txtUsuario = (TextView)findViewById(R.id.txtUsuario);txtPass = (TextView)findViewById(R.id.edPasswordCon);txtBaseDatos = (TextView)findViewById(R.id.txtBaseDatos);edConsulta = (EditText)findViewById(R.id.edConsulta);edResultado = (EditText)findViewById(R.id.edResultado); - Finalmente, dentro del evento onClick()
mostrarResultados , se conecta con el servidor MySQL y procesa la consulta realizada por el usuario, mostrando los resultados:
1234567891011121314151617181920212223242526public void mostrarResultados(View view){String consulta = edConsulta.getText().toString();String[] resultadoSQL = null;try{if(consulta.equals("")){Toast.makeText(this, "Debe indicar una consulta Transact-SQL válida.", Toast.LENGTH_LONG).show();}else{datosConexion = new String[]{txtServidor.getText().toString(),txtPuerto.getText().toString(),txtBaseDatos.getText().toString(),txtUsuario.getText().toString(),txtPass.getText().toString(),consulta};String driver = "com.mysql.jdbc.Driver";Class.forName(driver).newInstance ();resultadoSQL = new ConsultasAsincrona().execute(datosConexion).get();[...]
12345678910111213String resultadoConsulta = resultadoSQL[0];String numFilas = resultadoSQL[1];String numColumnas = resultadoSQL[2];edResultado.setText(resultadoConsulta + "Número de filas devueltas: " +numFilas + "\nNúmero de columnas devueltas: " + numColumnas);}}catch(Exception ex){Toast.makeText(this, "Error al obtener resultados de la consulta Transact-SQL: "+ ex.getMessage(), Toast.LENGTH_LONG).show();}}
ConsultasMySQL\app\src\main\java\com\academiaandroid\consultasmysql\ConsultasAsincrona.java
- Al igual que la clase descrita anteriormente, esta clase hereda de la clase base AsyncTask, y por lo tanto permite comunicarse con el hilo principal, y realizar tareas en segundo plano:
12public class ConsultasAsincrona extends AsyncTask<String[],Void,String[]> { - Se declaran tres variable de tipo
Connection
,Statement
yResulSet
:
1234private Connection conexionMySQL;private Statement st = null;private ResultSet rs = null; - Se sobrescribe el método
doInBackground , que será donde se defina el código que se ejecutará en segundo plano. Recibe como parámetro un array de strings declarados en la clase ConsultasSQL.java, con los datos de conexión y consulta a la base de datos, al llamar al método
execute(Params) . Este método devuelve una array de strings con los registros de la consulta, además del número de filas y columnas. Inicialmente se declaran e inicializan los datos de conexión al servidor:
123456789101112131415161718@Overrideprotected String[] doInBackground(String[]... datos){String sql = datos[0][5];String resultadoSQL = "";String[] totalResultadoSQL = null;int numColumnas;int numFilas;String SERVIDOR = datos[0][0];String PUERTO = datos[0][1];String BD = datos[0][2];String USUARIO = datos[0][3];String PASSWORD = datos[0][4];try{conexionMySQL = DriverManager.getConnection("jdbc:mysql://" + SERVIDOR + ":" + PUERTO + "/" + BD,USUARIO,PASSWORD); - A continuación, se ejecutará la consulta indicada en el campo edConsulta por el usuario:
12345st = conexionMySQL.createStatement();rs = st.executeQuery(sql);[...] - En el siguiente paso, se comprueba el número de filas devueltas por la consulta, para en caso de que sea superior a 0, se implemente un bucle que recorrerá y almacenará los resultados a partir de la consulta ejecutada:
12345678910111213141516171819if(rs.getRow() == 0){String mensajeError = "No se ha producido ningún resultado. Revise la consulta realizada.\n";totalResultadoSQL = new String[]{mensajeError, "0", "0"};}else{rs.beforeFirst();while (rs.next()){resultadoSQL += "ID-> " + rs.getString(1) +"\nDNI-> " + rs.getString(2) +"\nNombre y Apellidos-> " + rs.getString(3) + " " + rs.getString(4) +";\n";}[...]} - Finalmente se cierran las conexiones abiertas con el servidor:
12345678910111213141516171819finally{try{if(rs != null){rs.close();}st.close();conexionMySQL.close();} catch (SQLException e){e.printStackTrace();}}return totalResultadoSQL;}}
ConsultasMySQL\app\src\main\res\layout\activity_main.xml
- Se define un layout denominado ‘activity_main.xml‘ (layout de la clase MainActivity.java), que permitirá la inserción de datos en cuatro componentes de tipo EditText (dirección de servidor, nº de puerto, usuario y contraseña de la conexión al servidor), además de un componente de tipo Button para realizar la conexión y enviar los datos a la siguiente Activity:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/container"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.academiaandroid.consultasmysql.MainActivity"tools:ignore="MergeRootFrame" ><TableLayoutandroid:layout_width="match_parent"android:layout_height="match_parent" >[...]<TableRowandroid:id="@+id/tableRow2"android:layout_width="wrap_content"android:layout_height="wrap_content" ><EditTextandroid:id="@+id/edServidor"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ems="10"android:hint="@string/dirServidor" ><requestFocus /></EditText>[...]<TableRowandroid:id="@+id/tableRow6"android:layout_width="wrap_content"android:layout_height="wrap_content" ><Buttonandroid:id="@+id/btnConectar"style="?android:attr/buttonStyleSmall"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="abrirConexion"android:text="@string/mainConex" /></TableRow>[...]</TableLayout></FrameLayout>
ConsultasMySQL\app\src\main\res\layout\activityconsultassql.xml
- Se define un layout denominado ‘activityconsultassql.xml‘ (layout de la clase ConsultasSQL.java), que permitirá insertar consultas Transact-SQL dentro de un campo EditText, para posteriormente poder ejecutar dicha consulta pulsando el componente Button definido, mostrando a continuación el resultado de dicha consulta en un componente EditText. Además dentro de este layout se definen cuatro componentes de tipo TextView y un componente EditText para mostrar los datos de la conexión al servidor enviados desde la Activity anterior:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/container"android:layout<em>width="match</em>parent"android:layout<em>height="match</em>parent"tools:context="com.academiaandroid.consultasmysql.ConsultasSQL"tools:ignore="MergeRootFrame" ><TableLayoutandroid:layout_width="wrap_content"android:layout_height="match_parent"android:layout_marginLeft="15dip">[...]<TableLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content">[...]<TableRowandroid:id="@+id/tableRow3"android:layout_width="wrap_content"android:layout_height="wrap_content" ><EditTextandroid:id="@+id/edConsulta"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ems="10"android:hint="@string/consulta"android:layout_weight="2"android:onClick="consultaSelect" /></TableRow><TableRowandroid:id="@+id/tableRow4"android:layout_width="wrap_content"android:layout_height="wrap_content" ><Buttonandroid:id="@+id/btnEjecutar"style="?android:attr/buttonStyleSmall"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="mostrarResultados"android:text="@string/botonConsulta" />[...]</TableRow></TableLayout><TableRowandroid:id="@+id/tableRow6"android:layout_width="wrap_content"android:layout_height="wrap_content"><ScrollViewandroid:layout_width="fill_parent"android:layout_height="fill_parent"android:scrollbars="vertical"><EditTextandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:inputType="textMultiLine"android:ems="15"android:id="@+id/edResultado"android:background="#8438fdff"android:textStyle="bold|italic"android:hint="@string/resultado"android:editable="false" /></ScrollView></TableRow></TableLayout>
Consultas SQL
Consulta SQL para crear Base de datos tienda
:
1 2 |
CREATE DATABASE IF NOT EXISTS `tienda` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; USE `tienda`; |
Consulta SQL para crear la tabla cliente
:
1 2 3 4 5 6 7 8 |
CREATE TABLE IF NOT EXISTS `cliente` ( `id` int(11) NOT NULL AUTO_INCREMENT, `dni` varchar(9) NOT NULL, `nombre` varchar(25) NOT NULL, `apellidos` varchar(25) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `FK_Cliente` (`dni`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ; |
Consulta SQL para crear la tabla factura
:
1 2 3 4 5 6 |
CREATE TABLE IF NOT EXISTS `factura` ( `id` int(11) NOT NULL AUTO_INCREMENT, `dni_cliente` varchar(9) NOT NULL, `codigo_factura` int(5) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; |
Como se puede apreciar en la siguiente imagen, la base de datos tienda, estará formada por dos tablas llamadas cliente y factura, en las que se establece la relación entre el campo dni y el campo dni_cliente.
Además es necesario tener muy presente declarar dentro del AndroidManifest.xml
los permisos necesarios para la conexión con el servidor remoto, en este caso permisos de Internet:
1 |
<uses-permission android:name="android.permission.INTERNET" /> |
Descarga del proyecto
Aquí puedes descargar todo el código de este proyecto
DownloadEsta entrada tiene 13 comentarios
Los comentarios están cerrados.
Estimados, al descomprimir el archivo .rar me da unos errores.. podrian controlar y comentarme
Desde ya gracias
Marcelo
Hola Marcelo, podrías decirnos que errores son para investigarlo.
Puedes madárnoslo a nuestro correo de contacto: informacion@academiaandroid.com
Gracias
Ya encontre el problema.. el archivo rar esta OK. Mil disculpas.
Gracias
OK Marcelo, gracias por avisar porque tampoco a nosotros nos daba error al abrirlo.
Tengo el código exactamente igual, sin errores. Sin embargo, al ejecutarla en mi móvil la primera pantalla sale correctamente pero al meter todo me aparece lo siguiente:
Error al introducir los credenciales javanull pointer exception.
La dirección del servidor es correcta, la he visto en ipconfig, el puerto igual 3306 lo tengo en xaamp, y el usuario y la contraseña de la base de datos también está bien.
¿Qué crees que puede ser?
Recalcar que con el emulador si funciona, es con el móvil.
Hola Alejandro,
como ya nos enviaste una consulta como usuario Premium a nuestro correo, te comentamos mejor por ese medio.
Saludos
Hola,
Tengo una duda y me gustaría si podríais ayudarme.
Usando este tutorial, quiero obtener siempre el último valor de mi base de datos, el cuál lo hago con esta consulta:
String consulta = «SELECT * FROM valores WHERE ID =(SELECT max(ID) FROM valores)»;
Sin embargo, en vez de mostrarlo cada vez que pulse el botón, quiero hacer un bucle infinito para que esté continuamente mostrándome el último valor.
¿Cómo sería esto posible con AsyncTask? Ya que intento hacer un bucle infinito pero debo de tener un problema de hilos ya que una vez que lo ejecuto, queda mi pantalla en negro.
Muchas gracias.
Saludos
Hola Pedro,
sentimos decirte que no tenemos soporte técnico en esta web para este tipo de consultas. La ayuda la podemos ofrecer en nuestros cursos online donde dispones de un Tutor experto que resuelve tus dudas y revisa los ejercicios que vas realizando.
En cualquier caso, como excepción por ser usuario Premium, enviaremos tu consulta al tutor del curso Desarrollo de Aplicaciones para Android y si puede atenderla te contestaremos por email.
Saludos
Hola Pedro de nuevo,
te vamos a pegar aquí la respuesta que nos ha dado el Tutor de nuestro curso de Programación Android, Víctor Ramírez:
«Esto es una respuesta orientativa. Las tareas en background es algo que tratamos en el tema 4 del curso:
Un bucle infinito, como ya has comprobado por la pantalla en negro, bloquea el hilo principal de la aplicación y por tanto impide que la sigas utilizando, tal y como comentas necesitarías implementar la clase AsyncTask.
Puesto que tienes que compaginar la consulta de datos con la actualización de la interfaz se me ocurren dos opciones:
– O bien metes el bucle infinito en el método doInBackground() y utilizas el metodo OnProgressUpdate() para ir actaulizando el valor en la interfaz, de este modo tu AsyncTask no terminaria nunca.
– O realizas una única consulta en el doInBackground() y al finalizar ésta, en el onPostExecute() muestras el resultado por pantalla y reinicias de nuevo el AsyncTask
Personalmente veo mejor la segunda opción, aunque en cualquiera de los dos casos estas continuamente repitiendo la misma consulta a la base de datos lo cual no parece muy eficiente. Tal vez deberías plantearte que en el momento en que ese valor pueda cambiar (no se cuando ocurrirá esto en tu aplicación) lances la consulta para comprobar el valor máximo, aunque ya tendríamos que conocer la aplicación en más detalle para ver si esto es posible.»
Esperamos que te haya servido de ayuda.
un saludo
Muchas gracias por la respuesta, intentaré hacerlo.
Saludos
Finalmente pude conseguirlo con la segunda opción que recomendó Víctor Ramírez. Muchas gracias por la ayuda.
Perfecto, nos alegra que te haya servido, se lo diremos.
Un saludo