Coroutines en Android: Programación asíncrona moderna
Las coroutines de Kotlin son una herramienta poderosa para manejar operaciones asíncronas en Android. En este artículo, exploraremos cómo utilizarlas efectivamente en el desarrollo de aplicaciones Android.
¿Qué son las Coroutines?
Las coroutines son una forma de programación asíncrona que permite escribir código secuencial para operaciones asíncronas. En Android, son especialmente útiles para manejar operaciones de red, base de datos y otras tareas que no deben bloquear el hilo principal.
Conceptos básicos
1. Scope y Context
class MyViewModel : ViewModel() {
// Scope específico para ViewModel
private val viewModelScope = CoroutineScope(Dispatchers.Main + Job())
fun fetchData() {
viewModelScope.launch {
// Código asíncrono
}
}
}
2. Dispatchers
class Repository {
suspend fun fetchData() = withContext(Dispatchers.IO) {
// Operaciones de red o base de datos
}
suspend fun updateUI() = withContext(Dispatchers.Main) {
// Actualizaciones de UI
}
}
Patrones comunes
1. Manejo de errores
class UserRepository {
suspend fun getUser(id: String): Result<User> = try {
withContext(Dispatchers.IO) {
val user = api.getUser(id)
Result.success(user)
}
} catch (e: Exception) {
Result.failure(e)
}
}
// Uso
viewModelScope.launch {
userRepository.getUser("123")
.onSuccess { user ->
// Manejar éxito
}
.onFailure { error ->
// Manejar error
}
}
2. Operaciones paralelas
class DataManager {
suspend fun loadData() = coroutineScope {
val users = async { userRepository.getUsers() }
val posts = async { postRepository.getPosts() }
val result = Pair(users.await(), posts.await())
result
}
}
Integración con Android
1. ViewModel
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
try {
_users.value = repository.getUsers()
} catch (e: Exception) {
// Manejar error
}
}
}
}
2. Room Database
@Dao
interface UserDao {
@Query("SELECT * FROM users")
suspend fun getUsers(): List<User>
@Insert
suspend fun insertUser(user: User)
}
class UserRepository(
private val userDao: UserDao
) {
suspend fun getUsers() = withContext(Dispatchers.IO) {
userDao.getUsers()
}
}
Mejores prácticas
- Scopes apropiados
- Usar
viewModelScopeen ViewModels - Usar
lifecycleScopeen Activities/Fragments - Crear scopes personalizados cuando sea necesario
- Manejo de errores
- Implementar try-catch en operaciones críticas
- Usar
Resultpara encapsular resultados - Manejar errores de red específicamente
- Performance
- Elegir el dispatcher correcto
- Evitar operaciones bloqueantes
- Usar
withContextpara cambiar de contexto
Testing
@Test
fun testUserLoading() = runTest {
// Arrange
val repository = mock<UserRepository>()
val users = listOf(User("1", "Test"))
whenever(repository.getUsers()).thenReturn(users)
val viewModel = UserViewModel(repository)
// Act
viewModel.loadUsers()
// Assert
assertEquals(users, viewModel.users.value)
}
Ventajas de las Coroutines
- Código más legible y mantenible
- Mejor manejo de errores
- Integración con otras APIs de Kotlin
- Menos código boilerplate
- Mejor control del ciclo de vida
Conclusión
Las coroutines son una herramienta esencial para el desarrollo moderno de Android. Su capacidad para manejar operaciones asíncronas de manera elegante y eficiente las convierte en la opción preferida para la programación asíncrona en Kotlin.