Kotlin para desenvolvimento Android

1. Introdução ao Kotlin no Ecossistema Android

O Kotlin foi anunciado como linguagem oficial para desenvolvimento Android pelo Google em maio de 2017, durante o Google I/O. Desde então, tornou-se a escolha predominante para novos projetos, substituindo gradualmente o Java como linguagem primária. O ecossistema Android evoluiu para oferecer suporte nativo ao Kotlin, com o Android Studio fornecendo ferramentas de conversão automática, linting especializado e templates de projeto que já utilizam Kotlin por padrão.

As principais vantagens do Kotlin sobre Java incluem:

  • Concisão: Redução significativa de código boilerplate. Uma Activity em Kotlin pode ter 40% menos linhas que sua equivalente em Java.
  • Segurança contra null: O sistema de tipos do Kotlin elimina NullPointerExceptions em tempo de compilação.
  • Interoperabilidade total: Código Kotlin e Java podem coexistir no mesmo projeto, permitindo migração gradual.

O ecossistema atual inclui o Android Studio (IDE oficial), Jetpack (conjunto de bibliotecas Google), e bibliotecas Kotlin-first como Ktor, Exposed e Kotlinx.serialization.

2. Fundamentos da Linguagem Kotlin para Android

Variáveis e segurança contra null

// Declaração de variáveis
val nome: String = "Android"  // Imutável (recomendado)
var versao: Int = 14           // Mutável

// Tipos nullable (segurança contra null)
val descricao: String? = null  // Pode ser null
val tamanho: Int = descricao?.length ?: 0  // Elvis operator

// Uso seguro com let
descricao?.let {
    println("Descrição tem ${it.length} caracteres")
}

Funções e expressões de escopo

// Função simples
fun soma(a: Int, b: Int): Int = a + b

// Lambda
val quadrado: (Int) -> Int = { it * it }

// Expressões de escopo
val usuario = Usuario("João", 30).apply {
    // Configuração do objeto
    email = "joao@email.com"
    ativo = true
}

val nomeUsuario = usuario.let { 
    "Usuário: ${it.nome}"
}

Data classes e objetos singleton

// Data class - gera equals, hashCode, toString, copy automaticamente
data class Produto(
    val id: Int,
    val nome: String,
    val preco: Double
)

// Singleton com object
object Configuracao {
    val API_URL = "https://api.exemplo.com"
    const val TIMEOUT = 30_000L
}

3. Programação Orientada a Objetos e Funcional

Classes seladas e funções de extensão

// Sealed class para estados
sealed class Resultado<out T> {
    data class Sucesso<T>(val dados: T) : Resultado<T>()
    data class Erro(val mensagem: String) : Resultado<Nothing>()
    object Carregando : Resultado<Nothing>()
}

// Função de extensão
fun String.capitalizarPalavras(): String {
    return this.split(" ").joinToString(" ") { 
        it.replaceFirstChar { it.uppercase() } 
    }
}

// Uso
val texto = "kotlin android".capitalizarPalavras()  // "Kotlin Android"

Corrotinas para concorrência

// Função suspensa
suspend fun buscarDados(): List<Usuario> {
    return withContext(Dispatchers.IO) {
        api.getUsuarios()
    }
}

// Lançamento de corrotinas
fun carregarDados() {
    viewModelScope.launch {
        try {
            val resultado = buscarDados()
            _usuarios.value = resultado
        } catch (e: Exception) {
            _erro.value = e.message
        }
    }
}

// Execução paralela com async
val usuarios = async { repositorio.getUsuarios() }
val produtos = async { repositorio.getProdutos() }
val (listaUsuarios, listaProdutos) = awaitAll(usuarios, produtos)

4. Integração com Android SDK e Jetpack

Activity e ViewBinding

// ViewBinding no build.gradle
// android { buildFeatures { viewBinding = true } }

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.botaoSalvar.setOnClickListener {
            val nome = binding.editNome.text.toString()
            salvarUsuario(nome)
        }
    }
}

Android KTX - Extensões úteis

// SharedPreferences com KTX
val prefs = context.getSharedPreferences("config", Context.MODE_PRIVATE)
prefs.edit {
    putString("nome", "Android")
    putInt("versao", 14)
    apply()
}

// ViewModel com KTX
class MeuViewModel : ViewModel() {
    fun carregar() {
        viewModelScope.launch {
            // Lógica assíncrona
        }
    }
}

5. Gerenciamento de Estado e Reatividade

ViewModel com StateFlow

class UsuarioViewModel : ViewModel() {
    // StateFlow - estado observável
    private val _usuarios = MutableStateFlow<List<Usuario>>(emptyList())
    val usuarios: StateFlow<List<Usuario>> = _usuarios.asStateFlow()

    private val _carregando = MutableStateFlow(false)
    val carregando: StateFlow<Boolean> = _carregando.asStateFlow()

    fun carregarUsuarios() {
        viewModelScope.launch {
            _carregando.value = true
            try {
                val dados = repositorio.buscarUsuarios()
                _usuarios.value = dados
            } finally {
                _carregando.value = false
            }
        }
    }
}

// Observação na Activity/Fragment
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.usuarios.collect { lista ->
            adapter.submitList(lista)
        }
    }
}

Jetpack Compose - Visão geral

@Composable
fun TelaUsuario(viewModel: UsuarioViewModel = viewModel()) {
    val usuarios by viewModel.usuarios.collectAsState()
    val carregando by viewModel.carregando.collectAsState()

    if (carregando) {
        CircularProgressIndicator()
    } else {
        LazyColumn {
            items(usuarios) { usuario ->
                CardUsuario(usuario)
            }
        }
    }
}

6. Trabalhando com Dados e Redes

Retrofit com Kotlin Serialization

// Configuração do Retrofit
interface ApiService {
    @GET("usuarios")
    suspend fun getUsuarios(): List<Usuario>

    @POST("usuarios")
    suspend fun criarUsuario(@Body usuario: Usuario): Usuario
}

// Instância do Retrofit
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.exemplo.com/")
    .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
    .build()

val api = retrofit.create(ApiService::class.java)

Room com corrotinas

@Dao
interface UsuarioDao {
    @Query("SELECT * FROM usuarios")
    suspend fun getAll(): List<Usuario>

    @Insert
    suspend fun insert(usuario: Usuario)

    @Query("SELECT * FROM usuarios WHERE id = :id")
    fun getById(id: Int): Flow<Usuario?>  // Flow para reatividade
}

// Repositório
class UsuarioRepository(
    private val api: ApiService,
    private val dao: UsuarioDao
) {
    suspend fun sincronizar(): List<Usuario> {
        val remotos = api.getUsuarios()
        dao.insertAll(remotos)
        return dao.getAll()
    }
}

7. Testes, Depuração e Boas Práticas

Testes unitários com MockK

class UsuarioViewModelTest {
    private val repositorio = mockk<UsuarioRepository>()
    private val viewModel = UsuarioViewModel(repositorio)

    @Test
    fun `deve carregar usuarios com sucesso`() = runTest {
        // Configuração
        val usuariosMock = listOf(Usuario(1, "João"))
        coEvery { repositorio.buscarUsuarios() } returns usuariosMock

        // Execução
        viewModel.carregarUsuarios()

        // Verificação
        assertEquals(usuariosMock, viewModel.usuarios.value)
        coVerify { repositorio.buscarUsuarios() }
    }
}

Padrão MVVM com Clean Architecture

// Camada de domínio
class ObterUsuariosUseCase(
    private val repositorio: UsuarioRepository
) {
    suspend operator fun invoke(): Result<List<Usuario>> {
        return try {
            Result.success(repositorio.buscarUsuarios())
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

// ViewModel
class UsuarioViewModel(
    private val obterUsuarios: ObterUsuariosUseCase
) : ViewModel() {
    fun carregar() {
        viewModelScope.launch {
            obterUsuarios().fold(
                onSuccess = { _usuarios.value = it },
                onFailure = { _erro.value = it.message }
            )
        }
    }
}

8. Publicação e Tendências Futuras

Gradle Kotlin DSL

// build.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("org.jetbrains.kotlin.plugin.serialization")
}

android {
    compileSdk = 34
    defaultConfig {
        minSdk = 24
        targetSdk = 34
    }
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

O futuro do Kotlin no Android inclui o compilador K2 (prometendo builds até 2x mais rápidos), suporte multiplataforma (KMP) para compartilhar lógica entre Android e iOS, e WebAssembly (Wasm) para execução no navegador. A Google continua investindo pesadamente no ecossistema, com ferramentas como o Jetpack Compose se tornando o padrão para UI declarativa.

Referências