1
resposta

[Dúvida] try catch não funciona

Olá, implementei o try catch como recomendado no curso, porém ele não especifica o erro e apenas fecha o app quando não há conexão com a api

Código do repositório:

package br.com.alura.anyflix.network.repository

import android.util.Log
import br.com.alura.anyflix.database.dao.MovieDao
import br.com.alura.anyflix.database.entities.toMovie
import br.com.alura.anyflix.model.Movie
import br.com.alura.anyflix.network.services.MovieService
import br.com.alura.anyflix.network.services.toMovieEntity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.net.ConnectException
import java.net.SocketTimeoutException
import javax.inject.Inject
import kotlin.coroutines.coroutineContext

class MovieRepository @Inject constructor(
    private val dao: MovieDao,
    private val service: MovieService,
) {
    suspend fun findSections(): Flow<Map<String, List<Movie>>> {

        try {
            CoroutineScope(coroutineContext).launch {
                val response = service.findAll()
                val entities = response.map { it.toMovieEntity() }
                dao.saveAll(*entities.toTypedArray())
            }
        } catch (e: SocketTimeoutException) {
            Log.e("MovieRepository", "findSections: API demorou para responder", e)
        } catch (e: ConnectException) {
            Log.e("MovieRepository", "findSections: não foi possível se conectar à internet", e)
        } catch (e: Exception) {
            Log.e("MovieRepository", "findSections: ocorreu um erro ao conectar", e)
        }

        return dao.findAll()
            .map { entities ->
                val movies = entities.map { it.toMovie() }
                if (movies.isEmpty()) {
                    emptyMap()
                } else {
                    createSections(movies)
                }
            }
    }

    private fun createSections(movies: List<Movie>) = mapOf(
        "Em alta" to movies.shuffled().take(7),
        "Novidades" to movies.shuffled().take(7),
        "Continue assistindo" to movies.shuffled().take(7)
    )
    
    // outros códigos

Código do ViewModel da HomeScreen:

package br.com.alura.anyflix.ui.viewmodels

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import br.com.alura.anyflix.model.Movie
import br.com.alura.anyflix.network.repository.MovieRepository
import br.com.alura.anyflix.ui.uistates.HomeUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val repository: MovieRepository
) : ViewModel() {

    private var currentUiStateJob: Job? = null
    private val _uiState = MutableStateFlow<HomeUiState>(
        HomeUiState.Loading
    )
    val uiState = _uiState.asStateFlow()

    init {
        loadUiState()
    }

    private fun loadUiState() {
        currentUiStateJob?.cancel()
        currentUiStateJob = viewModelScope.launch {

            repository.findSections().onStart {
                _uiState.update { HomeUiState.Loading }
            }.collectLatest { sections ->
                if (sections.isEmpty()) {
                    delay(3000)
                    _uiState.update {
                        HomeUiState.Empty
                    }
                } else {
                    val movie = sections
                        .entries.random()
                        .value.random()
                    _uiState.update {
                        HomeUiState.Success(
                            sections = sections,
                            mainBannerMovie = movie
                        )
                    }
                }
            }
        }
    }

    fun loadSections() {
        loadUiState()
    }

}

Logcat quando fecha o app:

  FATAL EXCEPTION: main
  Process: br.com.alura.anyflix, PID: 12923
1 resposta

Olá, Juan.

Tudo bem?

Pelo que você compartilhou, parece que o problema está relacionado ao contexto de corrotina que você está usando no seu try-catch. No seu código, você está lançando uma nova corrotina dentro do try utilizando CoroutineScope(coroutineContext).launch. No entanto, se uma exceção ocorrer dentro dessa corrotina, ela não será capturada pelo try-catch que a envolve, pois as corrotinas são projetadas para lidar com exceções de maneira diferente.

Uma solução para o seu problema seria mover o try-catch para dentro do bloco launch, assim:

CoroutineScope(coroutineContext).launch {
    try {
        val response = service.findAll()
        val entities = response.map { it.toMovieEntity() }
        dao.saveAll(*entities.toTypedArray())
    } catch (e: SocketTimeoutException) {
        Log.e("MovieRepository", "findSections: API demorou para responder", e)
    } catch (e: ConnectException) {
        Log.e("MovieRepository", "findSections: não foi possível se conectar à internet", e)
    } catch (e: Exception) {
        Log.e("MovieRepository", "findSections: ocorreu um erro ao conectar", e)
    }
}

Dessa forma, qualquer exceção que ocorrer dentro da corrotina será capturada pelo try-catch.

Espero ter ajudado. Qualquer dúvida manda aqui. Bons estudos.