Cache de dados em Kotlin: como acelerar sua aplicação em 10 vezes sem mágica
Imagine: você abre seu aplicativo favorito e ele demora 5 segundos para carregar, mostrando uma "tela de espera" e um spinner infinito. Familiar? Agora imagine que o mesmo programa abre instantaneamente, mesmo sem internet. O segredo está no cache de dados bem feito. E hoje vamos analisar como implementá-lo em Kotlin para que seu código seja não apenas rápido, mas também elegante.
Kotlin é uma linguagem amada pela sua concisão e segurança. Mas mesmo na melhor linguagem, você pode escrever um aplicativo lento se não pensar em cache. Neste artigo, percorreremos o caminho desde um simples cache in-memory até soluções avançadas com Redis e DiskLruCache. Você aprenderá como armazenar dados em cache em Kotlin para que os usuários esqueçam os carregamentos e o servidor, as requisições desnecessárias.
Por que armazenar dados em cache em Kotlin?
Cache é como um bom hábito: difícil no começo, indispensável depois. Aqui estão três razões pelas quais seu aplicativo Kotlin precisa de cache:
- Velocidade: dados da memória são lidos em microssegundos, não em segundos como do servidor ou disco.
- Economia de tráfego: menos requisições à API significam menos gastos com servidores e internet móvel.
- Modo offline: os usuários adoram quando o aplicativo funciona até no metrô sem conexão.
Mas há um detalhe: o cache incorreto pode causar vazamentos de memória (OutOfMemoryError — um velho conhecido) ou exibir dados desatualizados. Portanto, vamos entender como fazer isso corretamente.
Cache in-memory: a maneira mais simples de acelerar seu aplicativo Kotlin
Vamos começar com o básico — cache na memória RAM. É como colocar as ferramentas necessárias na mesa de trabalho, em vez de ir buscá-las na garagem toda vez.
A opção mais simples é usar HashMap ou ConcurrentHashMap (se você trabalha com multithreading). Mas em Kotlin podemos fazer isso de forma mais elegante:
class SimpleCache<K, V>( private val maxSize: Int = 100) { private val cache = LinkedHashMap<K, V>(0, 0.75f, true) // access-order
fun get(key: K): V? { return cache[key] }
fun put(key: K, value: V) { if (cache.size >= maxSize) { val oldest = cache.keys.firstOrNull() oldest?.let { cache.remove(it) } } cache[key] = value }
fun clear() { cache.clear() }}Este cache remove automaticamente o elemento mais antigo quando atinge o limite. Mas há um problema: ele não considera o tempo de vida dos dados. Suponha que você armazenou em cache uma lista de taxas de câmbio e, uma hora depois, a taxa mudou, mas o usuário vê os dados antigos. Não é bom.
Adicionando TTL (Time To Live)
data class CacheEntry<T>( val data: T, val expirationTime: Long)
class TTLCache<K, V>( private val ttlMillis: Long = 60_000, // 1 minuto private val maxSize: Int = 100) { private val cache = LinkedHashMap<K, CacheEntry<V>>(0, 0.75f, true)
fun get(key: K): V? { val entry = cache[key] ?: return null if (System.currentTimeMillis() > entry.expirationTime) { cache.remove(key) return null } return entry.data }
fun put(key: K, value: V) { if (cache.size >= maxSize) { val oldest = cache.keys.firstOrNull() oldest?.let { cache.remove(it) } } cache[key] = CacheEntry(value, System.currentTimeMillis() + ttlMillis) }}Agora os dados vivem exatamente o tempo necessário. Ideal para armazenar em cache respostas de API onde os dados mudam com pouca frequência (por exemplo, lista de categorias de produtos).
Cache no Android: DiskLruCache e Room
Em dispositivos móveis, a memória é um recurso limitado. Se o seu