📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Especialização em Android Nativo. Recomendado revisar antes de começar.
Bem-vindo à quarta etapa da Fase 7: Arquitetura Avançada & Processamento em Background! 🤖
Neste projeto prático, você aprenderá a arquitetar rotinas de segundo plano persistentes e confiáveis no Android utilizando o Jetpack WorkManager — a biblioteca oficial recomendada pelo Google para tarefas assíncronas que precisam ser executadas com garantia de entrega, mesmo se o usuário fechar o aplicativo ou reiniciar o celular.
Com o tema futurista do sistema de inteligência artificial J.A.R.V.I.S. das Indústrias Stark, construiremos um fluxo de backup dos núcleos da armadura Mark 85. Você aprenderá a aplicar restrições inteligentes de hardware (Constraints), emitir progresso em tempo real através de setProgress() e observar o ciclo de vida da tarefa (ENQUEUED, RUNNING, SUCCEEDED) de forma reativa no Jetpack Compose!
| 1. Configuração & Ocioso | 2. Executando (RUNNING 75%) | 3. Sucesso (SUCCEEDED 100%) |
|---|---|---|
| Reator Arc em espera com seleção de Constraints. | Criptografia ativa, emissão de progresso e túnel TLS. | Conclusão atômica persistida com dados de telemetria. |
Antes de iniciar este laboratório, certifique-se de ter compreendido:
Ao concluir este projeto autoguiado, você será capaz de:
CoroutineWorker: Criar trabalhadores assíncronos não-bloqueantes com Kotlin Coroutines para operações de I/O e rede.Constraints): Configurar requisitos de execução inteligente, como exigir conexão Wi-Fi (NetworkType.UNMETERED) e bateria não baixa (setRequiresBatteryNotLow(true)).setProgress() dentro do Worker e sincronizar com a UI do Jetpack Compose via getWorkInfoByIdLiveData().setBackoffCriteria(BackoffPolicy.EXPONENTIAL) para proteger servidores contra sobrecargas em caso de oscilações de rede.graph TD
A[Usuário clica em DISPARAR PROTOCOLO] --> B[OneTimeWorkRequestBuilder com Constraints e Backoff]
B --> C[WorkManager.enqueue: Enfileira a tarefa no SQLite interno do SO]
C --> D{Restrições atendidas? Rede / Bateria}
D -- Não --> E[Estado: ENQUEUED - Aguardando hardware favorável]
D -- Sim --> F[Estado: RUNNING - Dispara DatabaseBackupWorker.doWork]
F --> G[Worker emite setProgress: 25%, 50%, 75%]
G --> H[Jetpack Compose atualiza Reator Arc e Barra de Progresso]
F -->|Sucesso| I[Result.success -> Estado: SUCCEEDED 100%]
F -->|Falha| J[Result.retry -> Aplica Backoff Exponencial e Reenfileira]
[!IMPORTANT] Vulnerabilidade de Esgotamento de Bateria e Recursos (Local DoS): No Android antigo, criar threads manuais ou
Servicesinfinitos em segundo plano esgotava a bateria do usuário e causava o encerramento forçado do app pelo sistema (Out of Memory Killer).Como o WorkManager e nossas práticas garantem segurança:
- Execução sob Demanda e Respeito ao Doze Mode: O WorkManager acorda a aplicação apenas quando as condições de energia e rede forem favoráveis.
- Retentativa com Backoff Exponencial: Impede ataques acidentais de negação de serviço (DDoS) contra a API das Indústrias Stark caso a conexão caia.
- Conexões Seguras: O tráfego de dados de backup exige canais criptografados HTTPS / TLS 1.3.
- Higiene de Versionamento: O arquivo
.gitignorebloqueia arquivos temporários de build e credenciais de ambiente.
| Termo / Componente | O que é e para que serve? |
|---|---|
| WorkManager | API do Android Jetpack para gerenciar tarefas em segundo plano adiáveis e com garantia de execução. |
| CoroutineWorker | Variante do Worker especializada em Kotlin Coroutines, permitindo suspensões nativas (delay, chamadas de rede assíncronas). |
| Constraints | Conjunto de regras de hardware que determinam quando o trabalho pode rodar (ex: Wi-Fi, carregando na tomada, bateria suficiente). |
| OneTimeWorkRequest | Requisição para executar uma tarefa uma única vez quando as restrições forem satisfeitas. |
| setProgress(workDataOf(…)) | Método para enviar relatórios parciais de progresso do trabalhador para a interface do usuário. |
| BackoffPolicy.EXPONENTIAL | Algoritmo que dobra o intervalo de espera entre tentativas sucessivas após uma falha de execução. |
File > New > New Project).J.A.R.V.I.S. Sync (ou android_p15_jarvis_workmanager)br.com.curso.jarvisKotlinAPI 24 ("Nougat"; Android 7.0) ou superiorGroovy DSL (build.gradle) ou Kotlin DSL (build.gradle.kts)build.gradle)Abra app > build.gradle e inclua o WorkManager KTX:
dependencies {
implementation platform('androidx.compose:compose-bom:2024.02.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose:1.8.2'
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
// WorkManager KTX (Processamento Seguro em Background)
implementation 'androidx.work:work-runtime-ktx:2.9.0'
}
Clique em Sync Now.
AndroidManifest.xml)Abra app > src > main > AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:label="J.A.R.V.I.S. Sync"
android:supportsRtl="true"
android:theme="@style/Theme.CartaoTreinador">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
DatabaseBackupWorker com Emissão de ProgressoAbra app > src > main > java > br > com > curso > jarvis > MainActivity.kt:
class DatabaseBackupWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
return try {
// Fase 1: Criptografia dos núcleos
setProgress(workDataOf("progresso" to 25, "etapa" to "Criptografando Núcleos de IA Mark 85..."))
delay(1200)
// Fase 2: Hash de integridade
setProgress(workDataOf("progresso" to 50, "etapa" to "Gerando Hash de Integridade SHA-256..."))
delay(1200)
// Fase 3: Transmissão TLS
setProgress(workDataOf("progresso" to 75, "etapa" to "Enviando Pacotes via Túnel TLS 1.3..."))
delay(1200)
// Fase 4: Conclusão
setProgress(workDataOf("progresso" to 100, "etapa" to "Sincronização com o Satélite Stark Concluída!"))
delay(800)
val outputData = workDataOf(
"tamanho" to "2.4 GB",
"servidor" to "Nuvem Stark - Torre de Nova York",
"timestamp" to System.currentTimeMillis()
)
Result.success(outputData)
} catch (e: Exception) {
Result.retry()
}
}
}
JarvisTheme)@Composable
fun JarvisTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = darkColorScheme(
primary = Color(0xFF00E5FF), // Ciano Reator Arc
secondary = Color(0xFFFFD54F), // Dourado Stark
tertiary = Color(0xFF00E676), // Verde Concluído
background = Color(0xFF070B11), // Preto Profundo HUD
surface = Color(0xFF0F1722),
surfaceVariant = Color(0xFF182333)
),
content = content
)
}
@Composable
fun JarvisScreen() {
val context = LocalContext.current
var workRequestId by remember { mutableStateOf<UUID?>(null) }
var modoWifiExclusivo by remember { mutableStateOf(false) }
// Observa o WorkInfo emitido pelo WorkManager
val workInfo by produceState<WorkInfo?>(initialValue = null, key1 = workRequestId) {
if (workRequestId != null) {
WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(workRequestId!!)
.observeForever { value = it }
}
}
val estadoTrabalho = workInfo?.state
val progressoPercent = workInfo?.progress?.getInt("progresso", 0) ?: if (estadoTrabalho == WorkInfo.State.SUCCEEDED) 100 else 0
// Disparo da Requisição com Constraints
Button(
onClick = {
val constraints = Constraints.Builder()
.setRequiredNetworkType(if (modoWifiExclusivo) NetworkType.UNMETERED else NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val workRequest = OneTimeWorkRequestBuilder<DatabaseBackupWorker>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
.addTag("stark_backup")
.build()
workRequestId = workRequest.id
WorkManager.getInstance(context).enqueue(workRequest)
},
enabled = estadoTrabalho != WorkInfo.State.RUNNING
) {
Text("DISPARAR PROTOCOLO STARK (WORKMANAGER)")
}
}
| Sintoma Observado | Causa Provável | Como Resolver |
|---|---|---|
A tarefa fica presa em ENQUEUED e não inicia |
As Constraints configuradas não foram atendidas pelo emulador/dispositivo (ex: sem conexão de rede ou bateria baixa). |
Certifique-se de que o emulador está conectado ao Wi-Fi ou relaxe a restrição para NetworkType.CONNECTED. |
IllegalStateException: WorkManager is not initialized properly |
Conflito na inicialização padrão do WorkManager. | Certifique-se de que a biblioteca androidx.work:work-runtime-ktx está devidamente declarada no build.gradle. |
| O progresso não atualiza na tela | O método setProgress() do Worker não foi chamado ou o getWorkInfoByIdLiveData não está sendo observado. |
Verifique se você está emitindo setProgress(workDataOf(...)) com delay() entre as etapas. |
| O Worker trava a tela da aplicação | Operações síncronas bloqueantes foram chamadas diretamente na thread principal. | Herde de CoroutineWorker em vez do legado Worker, executando as operações dentro de doWork(). |
PeriodicWorkRequestBuilder: Configure a rotina de backup para executar automaticamente uma vez a cada 24 horas usando PeriodicWorkRequestBuilder<DatabaseBackupWorker>(24, TimeUnit.HOURS).IntegrityCheckWorker) e encadeie a execução com WorkManager.beginWith(...).then(...).WorkManager.cancelWorkById(workRequestId).Disponível em: app/src/main/java/br/com/curso/jarvis/MainActivity.kt.
No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd proj_aplicacoes_full_stack/projetos/android_p15_jarvis_workmanager
./gradlew build
./gradlew test
# ou abrir no Android Studio e clicar em Run (Shift+F10)
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code ou Android Studio, abra o projeto diretamente pela pasta
proj_aplicacoes_full_stack/projetos/android_p15_jarvis_workmanager.