Cómo consumir una API Restful desde una aplicación Android desarrollada con Android Studio y Kotlin. Usaremos Retrofit y Gson para el acceso al APIRest. Definimos todos los ficheros Kotlin necesarios para consumir la API Restful, la configuración y las dependencias. Usaremos Jetpack Compose para mostrar el listado de elementos. Capturamos la pulsación en cada elemento a modo de ejemplo.
- Requisitos para desarrollar aplicación Android en Kotlin para acceso a base de datos mediante API Rest.
- Crear proyecto Kotlin en Android Studio y establecer las dependencias.
- Crear data class FacturaModelo.
- Crear clase Kotlin Util.
- Crear interfaz ServicioAPI.
- Crear data class FacturaEstado.
- Crear el View Model FacturaVistaModelo.
- Crear fichero Kotlin FacturaVista.
- Modificar el MainActivity para que ejecute el Modelo-Vista en el inicio.
- Probar app Android que muestra un listado de facturas consumiendo un servicio API Restful.
- Descarga del proyecto completo con el código fuente y el APK de instalación.
Requisitos para desarrollar aplicación Android en Kotlin para acceso a base de datos mediante API Rest
Servidor con servicio API Restful
Necesitaremos disponer de un servidor que proporcione los servicios de API Rest. En el siguiente tutorial explicamos cómo montar un servidor API Rest en PHP desde cero, con las acciones habituales de listar registros, insertar registros y eliminar registros:
Usaremos esta API Rest para la ampliación Android que desarrollaremos de acceso. En esta primera fase usaremos el método «listar» del API Rest anterior.
Antes de iniciar el desarrollo de la app Android con Kotlin y Android Studio, nos aseguraremos de que la API Rest funciona correctamente. Para ello, podemos usar Postman o el comando Curl o incluso un navegador web. En este caso, si ejecutamos el método «listar», nos debe mostrar todas las facturas de la base de datos MariaDB o MySQL a la que la API Rest ataca. Para el caso de la aplicación Android, al usar el servidor intermediario API Rest, debe ser agnóstica al motor de base de datos. Como decimos, si probamos el método «listar», nos mostrará un listado de todas las facturas en formato JSON:

En este caso, este es el formato del JSON que consumiremos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[ { "codigo": 1, "cliente": "Nombre del cliente", "importe": "1000,00", "fecha": "2024-04-23" }, { "codigo": 2, "cliente": "Cliente 2", "importe": "3223.76", "fecha": "2023-07-19" } ] |
En este caso, el JSON devolverá los campos: codigo, cliente, importe y fecha de cada factura.
Es importante conocer la URL de acceso a la API Rest, dado que la necesitaremos en el desarrollo de la App. En este caso, la URL base será:
proyectoa.com:8080/apirestfacturas/
Y el método para listar las facturas será:
listar.php
Android Studio y permisos de acceso a Internet
Para el desarrollo de la App usaremos Android Studio Ladybug con API 24 (targetSdk y compileSdk al nivel 35).
En el fichero AndroidManifest.xml estableceremos permisos para acceso del dispositivo a Internet, para ello añadiremos esta línea:
1 |
<uses-permission android:name="android.permission.INTERNET"/> |

Advertencia de seguridad para consumo de API Restful desde app Android
Dado que se trata de una prueba de concepto y un entorno de laboratorio para desarrollo, no aplicaremos medidas de seguridad, como el uso de tokens y HTTPS, para establecer una conexión segura desde nuestra App al servidor API Restful.
Al no usar HTTPS para acceso al servicio API Restful, si compilamos la aplicación Android, nos daría este error:
1 2 3 4 |
FATAL EXCEPTION: main Process: facturacion.com.facturacionretrofit, PID: 11026 java.net.UnknownServiceException: CLEARTEXT communication to localhost not permitted by network security policy at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:164) |
Debido a que Android Studio establece una directiva de seguridad para evitar el acceso no seguro (en texto plano) mediante HTTP a un servicio API Restful. Dado que estamos en desarrollo, podremos desactivar esta directiva editando el fichero AndroidManifest.xml de nuestra app y añadiendo la siguiente línea dentro del apartado «application»:
android:usesCleartextTraffic=»true»
Explicamos la solución a este error en este enlace más en detalle:
En el caso de una aplicación de producción SIEMPRE hay que establecer mecanismos de seguridad para que la conexión entre nuestra app y el servidor API Restful, así como la transferencia de información, sea totalmente segura. Conectaremos siempre mediante HTTPS para que el tráfico viaje cifrado y estableceremos tokens y cualquier otro mecanismo para asegurar la confidencialidad e integridad de la información.
El establecimiento de estos mecanismos de seguridad no es objeto de este tutorial.
Crear proyecto Kotlin en Android Studio y establecer las dependencias
Crearemos un proyecto en Android Studio eligiendo «Phone and Tablet» y «Empty Activity». Para este ejemplo usaremos únicamente el Activity principal (Main Activity), que será donde mostremos el listado de facturas proporcionado por el API Rest:

Introduciremos el nombre del proyecto, por ejemplo «ProyectoARetrofit», el Minimun SDK a «API 24» y el language «Kotlin DSL»:

Una vez creado el proyecto Android Studio con Kotlin, añadiremos las dependencias necesarias para el uso de las librerías Retrofit y Gson, que serán las que usemos para consumir el servicio API Rest. Para ello, abriremos el fichero build.gradle.kts y añadiremos las siguientes líneas:
1 2 |
implementation("com.squareup.retrofit2:converter-gson") implementation("com.squareup.retrofit2:retrofit") |
Tras añadir estas líneas, pulsaremos en «Sync Now» para que Android Studio se decargue los ficheros necesarios para la ejecución de estas librerías:

Tras la sincronización, Android Studio nos propondrá modificar la definición de la implementación de las librearías al nuevo formato, por lo que dejaremos las dos líneas anteriores en:
1 2 |
implementation(libs.converter.gson) implementation(libs.retrofit) |
Crear data class FacturaModelo
En primer lugar crearemos el modelo de datos para la factura. Para ello agregaremos un nuevo fichero a nuestro proyecto, de tipo Kotlin:

Eligiendo «Data class» y con el nombre FacturaModelo:

En esta data class definiremos los campos del objeto factura con el que queremos trabajar, los que devuelve el API Restful. Por ello, añadiremos el siguiente contenido:
1 2 3 4 5 6 7 8 9 10 11 |
package proyectoa.com.proyectoaretrofit.datos import java.util.Calendar import java.util.Date data class FacturaModelo( var codigo:Int?=0, var cliente:String?="", var fecha: Date?= Calendar.getInstance().time, var importe:Double?=0.0 ) |
Crear clase Kotlin Util
Añadiremos un fichero Kotlin, donde colocaremos todos los procedimientos que utilizaremos en otras clases (formatear número en moneda, mostrar mensaje Toast, escribir en el Logcat, etc.). Lo llamaremos Util.kt y tendrá el siguiente contenido:
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 |
package proyectoa.com.proyectoaretrofit.util import android.content.Context import android.util.Log import android.widget.Toast import java.text.NumberFormat import java.util.Locale // Muestra un mensaje emergente unos segundos fun MostrarMensaje(context: Context, mensaje: String, tiempoLargo : Boolean = false) { var tiempo = Toast.LENGTH_LONG if (!tiempoLargo) tiempo = Toast.LENGTH_LONG val toast = Toast.makeText(context, mensaje, tiempo) toast.show() } // Escribir línea en Logcat de Android Studio para depuración fun EscribirLog(tag: String = "ProyectoA", mensaje: String) { Log.d(tag, mensaje) } //Formatea un número para devolver separador de miles y decimal en ES fun FormatearNumeroSeparadorMiles(numero: Double, moneda: String = "€"): String { try { val formato = NumberFormat.getNumberInstance(Locale("es", "ES")) formato.minimumFractionDigits = 2 formato.maximumFractionDigits = 2 var numFormateado = formato.format(numero) + moneda return numFormateado } catch (e: Exception) { return "0,00" } } //Saber si un número tiene decimales fun NumeroTieneDecimales(numero: Double): Boolean { try { return numero % 1 != 0.0 } catch (e: Exception) { return false } } |
Crear interfaz ServicioAPI
Crearemos ahora una interfaz (Kotlin Interface) que será la que utilice las librerías Retrofit y Gson para el consumo del API Restful. Dicha interfaz será la encargada de acceder al servidor API Restful y la encargada de ejecutar el método de obtención de las facturas en una lista, llamando a listar.php del servidor API Restful.
La llamaremos ServicioAPI y tendrá el siguiente contenido:
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 |
package proyectoa.com.proyectoaretrofit.datos import proyectoa.com.proyectoaretrofit.util.EscribirLog import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.GET interface ServicioAPI { @GET("listar.php") suspend fun getFacturas(): List<FacturaModelo> companion object { private var servicioAPI: ServicioAPI? = null private var url: String = "http://proyectoa.com:8080/apirestfacturas/" fun getInstance(): ServicioAPI { if (servicioAPI == null) { try { servicioAPI = Retrofit.Builder() .baseUrl(url) .addConverterFactory(GsonConverterFactory.create()) .build() .create(ServicioAPI::class.java) } catch (e: Exception) { EscribirLog(mensaje = "Error al ejecutar servicioAPI: " + e.message) } } return servicioAPI!! } } } |
Crear data class FacturaEstado
Para establecer el estado de carga de modelo vista, crearemos una nueva data class Kotlin, que llamaremos FacturaEstado y tendrá el siguiente contenido:
1 2 3 4 5 6 |
package proyectoa.com.proyectoaretrofit.datos data class FacturaEstado( val facturas: List<FacturaModelo> = emptyList(), val estaCargando: Boolean = false ) |
Crear el View Model FacturaVistaModelo
Crearemos una clase Kotlin que será el componente Vista-Modelo (View Model), encargado de llamar al ServicioAPI y obtener el listado de facturas para devolverlo como respuesta. Se llamará FacturaVistaModelo y tendrá el siguiente contenido:
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 |
package proyectoa.com.proyectoaretrofit.datos import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch import proyectoa.com.proyectoaretrofit.util.EscribirLog class FacturaVistaModelo: ViewModel() { var state by mutableStateOf(FacturaEstado()) private set var response:List<FacturaModelo> by mutableStateOf((listOf())) private set init { try { viewModelScope.launch { state = state.copy( estaCargando = true ) val servicioAPI = ServicioAPI.getInstance() val listaFactura = servicioAPI.getFacturas() response = listaFactura state = state.copy( estaCargando = false, facturas = response ) } } catch (e: Exception) { EscribirLog(mensaje = "Error al cargar el Modelo-Vista: " + e.message) } } } |
Crear fichero Kotlin FacturaVista
Crearemos el fichero Kotlin que será el encargado de mostrar de forma visual en el MainActivity la lista de facturas obtenida y un pie indicando el número de facturas total. Se añaden los enventos de onClick en cada factura y en el pie, a modo de ejemplo. El fichero se llamara FacturaVista.kt y tendrá el siguiente contenido:
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
package proyectoa.com.proyectoaretrofit.datos import android.annotation.SuppressLint import android.content.Context import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Card import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import proyectoa.com.proyectoaretrofit.util.EscribirLog import proyectoa.com.proyectoaretrofit.util.FormatearNumeroSeparadorMiles import proyectoa.com.proyectoaretrofit.util.MostrarMensaje import java.text.NumberFormat import java.text.SimpleDateFormat import java.util.Locale @OptIn(ExperimentalFoundationApi::class) @SuppressLint("SimpleDateFormat") @Composable fun FacturaVista(facturaVistaModelo: FacturaVistaModelo, context: Context) { val state = facturaVistaModelo.state val formatoImporte = NumberFormat.getNumberInstance(Locale("es", "ES")) formatoImporte.minimumFractionDigits = 2 formatoImporte.maximumFractionDigits = 2 if (state.estaCargando) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } } LazyColumn( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceAround ) { stickyHeader { Text( "Facturas", fontSize = 14.sp ) } itemsIndexed(items = facturaVistaModelo.response) { _, item -> Card( modifier = Modifier .padding(8.dp) .fillMaxSize() .background(Color.White) .clickable { val mensaje = "Se ha pulsado en la factura " + item.codigo.toString() EscribirLog(mensaje = mensaje) MostrarMensaje(context, mensaje, tiempoLargo = true) } ) { Column( modifier = Modifier.padding(10.dp), verticalArrangement = Arrangement.Center, ) { Text( text = "[" + item.codigo.toString() + "] " + item.cliente.toString(), fontSize = 13.sp ) Text( " → Fecha: " + item.fecha?.let { SimpleDateFormat("dd-MM-yyyy").format( it ) }, fontSize = 12.sp ) Text( text = " → Importe: " + item.importe?.let { FormatearNumeroSeparadorMiles( it ) }, fontSize = 11.sp ) } } } } Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier .fillMaxWidth() .align(Alignment.BottomCenter) .background(Color.LightGray) .padding(17.dp) .clickable { val mensaje = "Se ha pulsado en el pie de la lista de facturas" EscribirLog(mensaje = mensaje) MostrarMensaje(context, mensaje) } ) { Text("Número de facturas: " + facturaVistaModelo.response.count().toString(), fontSize = 12.sp, textAlign = TextAlign.Right, modifier = Modifier.clickable { val mensaje = "Se ha pulsado en el texto del pie de la lista de facturas" EscribirLog(mensaje = mensaje) MostrarMensaje(context, mensaje) }) } } } |
Modificar el MainActivity para que ejecute el Modelo-Vista en el inicio
Editaremos el fichero MainActivity para ejecutar el Modelo-Vista FacturaVistaModelo al iniciar la aplicación, quedará con el siguiente contenido:
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 |
package proyectoa.com.proyectoaretrofit import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels import proyectoa.com.proyectoaretrofit.datos.FacturaVista import proyectoa.com.proyectoaretrofit.datos.FacturaVistaModelo class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val vistaModelo: FacturaVistaModelo by viewModels() setContent { FacturaVista(facturaVistaModelo = vistaModelo, this) } /* enableEdgeToEdge() setContent { ProyectoARetrofitTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Greeting( name = "Android", modifier = Modifier.padding(innerPadding) ) } } } */ } } /* @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { ProyectoARetrofitTheme { Greeting("Android") } } */ |
Probar app Android que muestra un listado de facturas consumiendo un servicio API Restful
Una vez que tenemos todos los ficheros anteriores, podremos compilar la aplicación en el dispositivo virtual y comprobar que funciona correctamente:

Para realizar la prueba en un dispositivo smartphone Android real podemos generar el fichero APK, desde el menú «Build» – «Build App Bundle(s) / APK(s)» – «Build APK(s)»:

Una vez generado el APK lo enviaremos al móvil (por email, Telegram, WhatsApp, USB o cualquier otro medio:

Ejecutaremos el fichero .apk en el móvil y permitiremos su instalación. Nos advertirá de que no proviene de Play Store, pulsaremos en «Instalar sin analizar»:

Abriremos la aplicación:

La app conectará con el servidor API Restful y obtendrá y mostrará el listado de todas las facturas con los datos código, cliente, fecha e importe:

Si pulsamos en una factura, nos mostrará un mensaje con el código de la factura en la que hayamos tocado:

Si pulsamos en el pie, donde aparecen el número de facturas, también nos mostrará un mensaje indicando que hemos tocado en el pie:

Además, el control permite hacer scroll cuando las facturas no caben en la pantalla, podremos subir y bajar para verlas todas:

Descarga del proyecto completo con el código fuente y el APK de instalación
La descarga del código fuente en Kotlin del proyecto completo en Android Studio Ladybug 2024.2.2: