📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados na Especialização em Android Nativo. Recomendado revisar antes de iniciar.
v1.0 — Android Jetpack, Kotlin, Room Database (SQLite), Padrão DAO, Coroutines e StateFlow Reativo
Série Prática Android Clássico — Projeto 6 de 21
- P05: Quiz Game com Cronômetro Decrescente
- ➡️ P06 (este): Catálogo Offline com Room Database & Coroutines
- P07: Master Pokédex com Retrofit 2 & REST API
- P08: Batalha Scanner com CameraX & QR Code
- P09: PokeMap com Google Maps & GPS
O projeto Meus Jogos Favoritos aborda a camada de persistência estruturada recomendada pelo Google no ecossistema Android moderno: o Room Persistence Library. A aplicação implementa uma camada de abstração elegante sobre o SQLite relacional, com validação de consultas SQL em tempo de compilação, operações assíncronas em segundo plano com Kotlin Coroutines (Dispatchers.IO) e fluxo de dados reativo contínuo via Flow / StateFlow.
Os modelos, DAOs, base de dados e componentes de interface estão organizados na subpasta canônica mobile-android-kotlin/:
catalogo_projetos/android_p06_jogos_room/
├── assets/
│ └── arquitetura_jogos_room.svg
├── mobile-android-kotlin/
│ ├── app/
│ │ ├── src/
│ │ │ └── main/
│ │ │ ├── java/br/com/curso/jogosroom/
│ │ │ │ ├── data/
│ │ │ │ │ ├── JogoEntity.kt
│ │ │ │ │ ├── JogoDao.kt
│ │ │ │ │ └── AppDatabase.kt
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ └── build.gradle
│ ├── gradle/
│ ├── build.gradle
│ ├── gradlew
│ ├── settings.gradle
│ └── README.md
└── index.md
| ID | Persona | Narrativa da Funcionalidade | Critério de Aceitação Principal | Prioridade |
|---|---|---|---|---|
| US-01 | Gamer / Colecionador | Quero cadastrar novos jogos informando título, plataforma e nota de avaliação. | Gravação imediata no banco Room e atualização automática da lista visual. | Alta |
| US-02 | Gamer / Colecionador | Quero visualizar todos os jogos cadastrados mesmo com o smartphone em modo avião. | Recuperação dos registros locais a partir da tabela SQLite sem depender de rede. | Alta |
| US-03 | Gamer / Colecionador | Quero filtrar ou ordenar jogos por plataforma ou melhor nota. | Consulta SQL reativa filtrando itens em tempo real. | Média |
| US-04 | Gamer / Colecionador | Quero excluir jogos desinstalados da minha lista com confirmação. | Remoção atômica do registro no banco através do método anotado @Delete. |
Média |
| US-05 | Desenvolvedor Mobile | Quero que as operações de I/O nunca ocorram na thread principal para evitar ANR. | Execução estrita das funções suspensas no despachante Dispatchers.IO. |
Baixa |
@Entity(tableName = "jogos") com chave primária auto-incremental.@Dao com métodos anotados @Insert, @Delete e consultas @Query.Flow<List<JogoEntity>> observado pela ViewModel.@Database com padrão Singleton thread-safe para acesso ao Room.fallbackToDestructiveMigration para dev).flowchart TD
User([fa:fa-user Usuário / Colecionador])
Room[(fa:fa-database Room SQLite)]
subgraph AppRoom ["💾 Catálogo de Jogos (mobile-android-kotlin)"]
UC01["Cadastrar Novo Jogo"]
UC02["Listar Jogos em Tempo Real"]
UC03["Filtrar por Plataforma"]
UC04["Excluir Registro de Jogo"]
UC05["Consultar Estatísticas Locais"]
end
User --> UC01
User --> UC02
User --> UC03
User --> UC04
UC01 --> Room
Room -.->|Flow| UC02
UC04 --> Room
| ID Requisito | História de Usuário | Artefato de Código | Teste de Aceitação |
|---|---|---|---|
| RF-01 | US-01 | mobile-android-kotlin/app/.../JogoEntity.kt |
testEntityTableMapping() |
| RF-02 | US-01, US-04 | mobile-android-kotlin/app/.../JogoDao.kt |
testInsertAndQueryGameDao() |
| RF-03 | US-02 | mobile-android-kotlin/app/.../JogoDao.kt |
testFlowEmitsOnNewRecord() |
| RF-04 | US-05 | mobile-android-kotlin/app/.../AppDatabase.kt |
testDatabaseSingletonInstance() |
| RF-05 | US-01 | mobile-android-kotlin/app/.../MainActivity.kt |
testFormValidationOnSubmit() |
JogoDatabase.kt)📄 mobile-android-kotlin/app/src/main/java/br/com/curso/colecaojogos/data/local/JogoDatabase.kt (arquivo real do projeto)
package br.com.curso.colecaojogos.data.local
import android.content.Context
import androidx.room.*
import kotlinx.coroutines.flow.Flow
// 1. A TABELA (Entity)
@Entity(tableName = "jogos")
data class Jogo(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val nome: String,
val plataforma: String
)
// 2. O CONTROLE REMOTO (DAO)
@Dao
interface JogoDao {
@Insert
suspend fun inserir(jogo: Jogo)
@Query("SELECT * FROM jogos ORDER BY id DESC")
fun listarTodos(): Flow<List<Jogo>>
@Delete
suspend fun deletar(jogo: Jogo)
}
// 3. A CENTRAL (Database)
@Database(entities = [Jogo::class], version = 1)
abstract class JogoDatabase : RoomDatabase() {
abstract fun jogoDao(): JogoDao
companion object {
@Volatile
private var INSTANCE: JogoDatabase? = null
fun getDatabase(context: Context): JogoDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
JogoDatabase::class.java,
"jogo_database"
).build()
INSTANCE = instance
instance
}
}
}
}
cd catalogo_projetos/android_p06_jogos_room/mobile-android-kotlin
./gradlew test
./gradlew connectedAndroidTest
| Checkpoint | Meta Técnica | Critério de Aceite | Status |
|---|---|---|---|
| CP-01 | Esquema Room Compilado | Entidades e DAOs gerando classes de implementação via KSP | PASS ✅ |
| CP-02 | Persistência Offline | Dados persistem após fechar e reabrir o aplicativo | PASS ✅ |
| CP-03 | Fluxo Reativo com Flow | UI atualizada automaticamente ao inserir ou excluir itens | PASS ✅ |
| CP-04 | Coroutines Dispatchers.IO | Zero chamadas de banco bloqueando a thread principal | PASS ✅ |
| CP-05 | Suíte de Testes Automatizados | Testes de unidade em memória com Room.inMemoryDatabaseBuilder |
PASS ✅ |