O que android-kotlin faz?
Desenvolvimento Android Kotlin com Coroutines, Jetpack Compose, Hilt e testes MockK
Visão Geral
Esta skill fornece conhecimento especializado para desenvolvimento de aplicativos Android modernos usando Kotlin como linguagem principal, com foco em:
- Coroutines para concorrência e tarefas assíncronas
- Jetpack Compose para construção de interfaces de usuário declarativas
- Hilt para injeção de dependência
- MockK para testes unitários e de integração
Tecnologias Abordadas
Coroutines
- Lançamento de corrotinas com
viewModelScope,lifecycleScopeeGlobalScope - Uso de
async/awaitpara execução paralela - Tratamento de exceções com
try/catcheCoroutineExceptionHandler - Flow frio (
flow,flowOf) e quente (SharedFlow,StateFlow) - Operadores como
map,filter,catch,collect - Dispatchers (
Dispatchers.IO,Dispatchers.Main,Dispatchers.Default)
Jetpack Compose
- Funções
@Composablee@Preview - Gerenciamento de estado com
remember,mutableStateOf,StateFlow - Layouts:
Column,Row,Box,LazyColumn,LazyRow - Componentes Material3:
Button,TextField,Card,Scaffold,TopAppBar - Navegação com
NavControllereNavHost - Tema com
MaterialTheme(cores, tipografia, formas) - Modificadores (
Modifier.padding,Modifier.fillMaxWidth,Modifier.clickable) - Efeitos colaterais com
LaunchedEffect,DisposableEffect,SideEffect
Hilt
@HiltAndroidApppara aplicação@HiltViewModelpara ViewModels injetáveis@Injectpara construtores e campos- Módulos com
@Modulee@InstallIn - Providers com
@Providese@Binds - Escopos:
@Singleton,@ViewModelScoped,@ActivityScoped,@FragmentScoped @EntryPointpara classes que não são suportadas por Hilt
MockK
- Criação de mocks:
mockk<>(),spyk<>() - Definição de comportamento:
every { ... } returns ...,coEvery { ... } - Verificação de chamadas:
verify { ... },coVerify { ... },verifySequence - Captura de argumentos:
slot<>(),capture() - Relâmpago de regras:
useRelaxed = true - Teste com ViewModel:
runTest,TestCoroutineDispatcher - Uso com Compose:
ComposeTestRule - Mocks parciais com
spyk
Estrutura Recomendada de Projeto
app/
├── src/
│ ├── main/
│ │ ├── java/com/example/app/
│ │ │ ├── MainActivity.kt
│ │ │ ├── ui/
│ │ │ │ ├── screens/
│ │ │ │ ├── components/
│ │ │ │ └── theme/
│ │ │ ├── viewmodel/
│ │ │ ├── data/
│ │ │ │ ├── repository/
│ │ │ │ ├── local/
│ │ │ │ └── remote/
│ │ │ ├── di/
│ │ │ └── model/
│ │ └── res/
│ └── test/
│ └── java/com/example/app/
│ ├── viewmodel/
│ └── repository/
└── build.gradle.kts
Dependências comuns (build.gradle.kts)
dependencies {
// Jetpack Compose
implementation("androidx.compose.ui:ui:1.5.0")
implementation("androidx.compose.material3:material3:1.1.0")
implementation("androidx.compose.ui:ui-tooling-preview:1.5.0")
implementation("androidx.activity:activity-compose:1.7.0")
implementation("androidx.navigation:navigation-compose:2.7.0")
// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.0")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0")
// Hilt
implementation("com.google.dagger:hilt-android:2.47")
kapt("com.google.dagger:hilt-compiler:2.47")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
// Testes
testImplementation("io.mockk:mockk:1.13.5")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.0")
testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.5.0")
}
Exemplo de Uso
ViewModel com Hilt e Coroutines
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: MyRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
init {
loadData()
}
fun loadData() {
viewModelScope.launch {
_uiState.value = UiState.Loading
try {
val data = repository.fetchData()
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "Erro desconhecido")
}
}
}
}
Tela com Jetpack Compose
@Composable
fun MainScreen(viewModel: MainViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsState()
when (val state = uiState) {
is UiState.Loading -> CircularProgressIndicator()
is UiState.Success -> ItemList(state.data)
is UiState.Error -> Text("Erro: ${state.message}")
}
}
Teste com MockK
class MainViewModelTest {
private val repository: MyRepository = mockk()
private lateinit var viewModel: MainViewModel
@Before
fun setup() {
coEvery { repository.fetchData() } returns listOf()
viewModel = MainViewModel(repository)
}
@Test
fun `loadData deve retornar sucesso`() = runTest {
val data = listOf(Item("Teste"))
coEvery { repository.fetchData() } returns data
viewModel.loadData()
assertEquals(UiState.Success(data), viewModel.uiState.value)
}
}
Boas Práticas
- Utilize
StateFlowno ViewModel ecollectAsState()no Compose - Injete dependências via Hilt sempre que possível
- Prefira
viewModelScopepara lançar corrotinas em ViewModels - Use
coEveryecoVerifypara funçõessuspendem MockK - Estruture a UI em componentes reutilizáveis
- Use
@Previewpara visualizar componentes isoladamente - Mantenha o ViewModel livre de dependências do Android (exceto
Application) - Teste ViewModels com
runTesteTestCoroutineDispatcher