Background Thread no desenvolvimento mobile: o que é, tarefas e formas de uso

Autor: IT Sectr Publicado: 2026-03-15 Tempo de leitura: 11 min

Background Thread — uma thread de execução não vinculada à interface do usuário, projetada para operações de longa duração: requisições de rede, trabalho com arquivos, análise JSON, compressão de imagens, criptografia e consultas a banco de dados. No iOS, as threads em segundo plano são gerenciadas através de GCD (DispatchQueue.global) e OperationQueue; no Android, através de Executors, WorkManager e Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default). De acordo com a Documentação da Apple DispatchQueue, após a conclusão de uma operação em segundo plano, o resultado deve ser retornado à Main Thread para atualizar a interface.

Pontos principais

  • Background Thread realiza operações que bloqueiam a UI: rede, arquivos, JSON, cálculos
  • iOS: DispatchQueue.global(qos:) e OperationQueue para tarefas em segundo plano
  • Android: Dispatchers.IO (rede/arquivos), Dispatchers.Default (cálculos), WorkManager (tarefas em segundo plano)
  • Corrotinas — o padrão moderno para trabalho em segundo plano: withContext(Dispatchers.IO) alterna de thread sem callback hell
  • Resultado da Background Thread é sempre retornado à Main Thread para atualizar a UI

O que é uma Background Thread

Background Thread — qualquer thread em um aplicativo que não seja a Main Thread e não tenha acesso à UI. Sua tarefa é liberar a thread principal de operações pesadas para que a interface permaneça responsiva. O sistema operacional distribui as threads em segundo plano pelos núcleos da CPU, permitindo executar várias tarefas em paralelo. O iOS gerencia automaticamente o pool de threads através do GCD; o Android, através dos pools de Java Executors.

Ao contrário da Main Thread, que processa eventos sequencialmente (um após o outro), as threads em segundo plano podem executar em paralelo, limitadas apenas pelo número de núcleos da CPU. Por exemplo, em um dispositivo de 8 núcleos, é possível executar até 8 tarefas em segundo plano sem desaceleração significativa. No entanto, uma quantidade excessiva de threads (centenas) leva ao thread starvation — competição pelos núcleos e aumento da sobrecarga de troca de contexto (context switch).

Quality of Service (QoS) — mecanismo do iOS que permite especificar a prioridade de uma tarefa em segundo plano. Valores: .userInteractive (o mais alto, quase Main Thread), .userInitiated (o usuário aguarda o resultado), .default (padrão), .utility (o usuário não espera diretamente), .background (o mais baixo, para sincronização e indexação). No Android, o equivalente é Thread.setPriority() de 1 a 10, mas o Android também usa cgroups para gerenciamento em grupo de prioridades de threads.

Background Thread no iOS: GCD e DispatchQueue.global

DispatchQueue.global(qos:) — a principal forma de obter uma fila em segundo plano no iOS. O GCD (Grand Central Dispatch) cria automaticamente um pool de threads e distribui as tarefas pelos núcleos. A chamada DispatchQueue.global(qos: .background).async {} envia um bloco para a fila em segundo plano com a prioridade mais baixa. Para tarefas cujo resultado é necessário imediatamente, use .userInitiated ou .utility.

OperationQueue — uma abstração de mais alto nível sobre o GCD que permite definir dependências entre operações, o número máximo de operações simultâneas (maxConcurrentOperationCount) e prioridades. A OperationQueue é útil para cadeias multitarefa complexas: baixar arquivo → descompactar → salvar em cache. Por padrão, a OperationQueue usa threads em segundo plano, a menos que especificado de outra forma.

swift
import UIKit

class ImageDownloader {

    func downloadImagesSequentially() {
        let urls = ["https://example.com/1.png", "https://example.com/2.png"]

        // OperationQueue com maxConcurrentOperationCount = 2
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 2
        queue.qualityOfService = .utility

        for urlString in urls {
            queue.addOperation {
                guard let url = URL(string: urlString),
                      let data = try? Data(contentsOf: url)
                else { return }

                DispatchQueue.main.async {
                    print("Carregado: \(url.lastPathComponent)")
                }
            }
        }
    }

    // GCD: fila global em segundo plano com diferentes QoS
    func backgroundTaskWithQoS() {
        DispatchQueue.global(qos: .userInitiated).async {
            // Prioridade alta — o usuário aguarda o resultado
            let result = self.heavyComputation()
            DispatchQueue.main.async {
                self.showResult(result)
            }
        }
    }

    private func heavyComputation() -> String {
        Thread.sleep(forTimeInterval: 2) // simulando trabalho
        return "Resultado do cálculo"
    }

    private func showResult(_ result: String) {
        print("Result on Main: \(result)")
    }
}

No exemplo, a OperationQueue carrega duas imagens em paralelo (maxConcurrentOperationCount = 2) com QoS de fundo via qualityOfService = .utility. O método GCD backgroundTaskWithQoS usa uma fila global com .userInitiated para uma tarefa cujo resultado o usuário está aguardando. Ambas as abordagens terminam retornando à DispatchQueue.main para atualizar a UI — este é um requisito obrigatório no iOS.

Filas em segundo plano Serial vs Concurrent

O GCD suporta dois tipos de filas: seriais e concorrentes. Filas seriais executam tarefas uma após a outra — isso é conveniente para acessar um recurso compartilhado (arquivo, BD) sem bloqueios. Filas concorrentes executam tarefas em paralelo, distribuindo-as pelos núcleos disponíveis. DispatchQueue.global é sempre concorrente. Para criar uma fila serial, use DispatchQueue(label: "com.app.queue").

Background Thread no Android: Executors e Dispatchers

Android fornece vários níveis de abstração para threads em segundo plano. A abordagem clássica é java.util.concurrent.Executors.newFixedThreadPool(n) ou Executors.newCachedThreadPool(). A abordagem moderna são as Kotlin Coroutines com Dispatchers.IO (para E/S: rede, arquivos, BD) e Dispatchers.Default (para tarefas intensivas de CPU: ordenação, processamento de imagens). WorkManager é para tarefas em segundo plano adiadas e garantidas.

HandlerThread — uma classe especializada do Android para criar uma thread em segundo plano com seu próprio Looper (fila de mensagens). Ao contrário dos Executors, HandlerThread permite enviar mensagens e Runnables através de um Handler. É usado para operações que exigem enfileiramento (por exemplo, escrita sequencial em BD). Após o uso, deve-se chamar quit() ou quitSafely() para liberar recursos.

kotlin
// Android: Executors e Dispatchers de Corrotinas
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors

class DataRepository {

    private val ioExecutor = Executors.newFixedThreadPool(4)

    // Abordagem clássica via Executors
    fun loadDataLegacy(callback: (String) -> Unit) {
        ioExecutor.execute {
            val result = readFromFile()
            val handler = android.os.Handler(android.os.Looper.getMainLooper())
            handler.post { callback(result) }
        }
    }

    // Abordagem moderna via Corrotinas
    suspend fun loadDataCoroutines(): String {
        return withContext(Dispatchers.IO) {
            // Operação de arquivo — executando no pool em segundo plano
            readFromFile()
        }
        // Resultado retorna automaticamente ao Dispatchers.Main
    }

    // Tarefa intensiva de CPU no Dispatchers.Default
    suspend fun processImage(pixels: IntArray): IntArray {
        return withContext(Dispatchers.Default) {
            // Ordenação, filtragem — executando no pool Default
            pixels.sortedArray()
        }
    }

    private fun readFromFile(): String {
        Thread.sleep(1000) // simulando leitura de arquivo
        return "file_content"
    }

    fun cleanup() {
        ioExecutor.shutdown()
    }
}

O exemplo DataRepository mostra a evolução das threads em segundo plano no Android. O método legado loadDataLegacy usa Executors.newFixedThreadPool(4) com um Handler para retornar à Main Thread. O método moderno loadDataCoroutines usa withContext(Dispatchers.IO) — a corrotina é suspensa durante a execução sem bloquear a thread e retoma automaticamente na Main Thread. Dispatchers.Default é recomendado para operações intensivas de CPU (ordenação, filtragem, transformação de dados).

Corrotinas como padrão moderno de tarefas em segundo plano

Kotlin Coroutines — não apenas uma forma de trabalhar com threads, mas um modelo fundamentalmente diferente: tarefas assíncronas não estão vinculadas a uma thread específica e podem ser suspensas sem bloquear. Isso significa que, enquanto está em segundo plano, uma corrotina não ocupa uma thread, mas a libera para outras tarefas. O mecanismo de suspensão permite executar centenas de milhares de tarefas concorrentes em um pool de 4–8 threads sem thread starvation.

Três dispatchers principais: Dispatchers.Main (UI, uma thread), Dispatchers.IO (64 threads por padrão para operações bloqueantes: rede, arquivos, BD), Dispatchers.Default (igual ao número de núcleos da CPU, para cálculos intensivos). Combinando-os através de withContext, o desenvolvedor alterna entre threads sem criar callbacks. withContext é uma função suspend que não retorna o controle até que a tarefa seja concluída.

kotlin
// Corrotinas: composição de tarefas em segundo plano
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay

suspend fun loadUserProfile(userId: String): UserProfile =
    coroutineScope {
        // Carregamento paralelo de dados de diferentes fontes
        val user = async(Dispatchers.IO) { fetchUser(userId) }
        val posts = async(Dispatchers.IO) { fetchPosts(userId) }
        val avatar = async(Dispatchers.Default) {
            processAvatar(fetchAvatar(userId))
        }

        // await() — suspende até que todas as tarefas sejam concluídas
        UserProfile(
            user = user.await(),
            posts = posts.await(),
            avatar = avatar.await()
        )
    }

data class UserProfile(
    val user: String,
    val posts: List<String>,
    val avatar: ByteArray
)

suspend fun fetchUser(id: String): String { delay(300); return "User:$id" }
suspend fun fetchPosts(id: String): List<String> { delay(500); return listOf("Post1") }
suspend fun fetchAvatar(id: String): ByteArray { delay(200); return ByteArray(1024) }
suspend fun processAvatar(data: ByteArray): ByteArray { delay(100); return data }

A função loadUserProfile lança três tarefas em segundo plano em paralelo através de async. fetchUser e fetchPosts são de E/S (rede), executadas em Dispatchers.IO. processAvatar é intensiva de CPU (processamento de imagem), executada em Dispatchers.Default. await() suspende a corrotina até que todas as tarefas sejam concluídas. O tempo total de execução é igual ao tempo máximo entre as três tarefas (500 ms para fetchPosts), não a soma delas. Esta é uma vantagem fundamental das corrotinas sobre a execução sequencial.

Concorrência estruturada: prevenção de vazamentos

Structured concurrency — princípio pelo qual cada corrotina tem um escopo pai, e cancelar o pai cancela automaticamente as corrotinas filhas. No Android, lifecycleScope cancela todas as corrotinas quando a Activity é destruída. viewModelScope faz isso quando o ViewModel é limpo. Isso evita vazamentos de tarefas em segundo plano: se o usuário fechar a tela, enquanto estiver em segundo plano a corrotina não continuará carregando dados que não são mais necessários.

WorkManager: tarefas em segundo plano para Android

WorkManager — uma biblioteca Android Jetpack para executar tarefas em segundo plano que devem ser concluídas mesmo após reiniciar o dispositivo ou fechar o aplicativo. Ao contrário de Executors e corrotinas, que vivem dentro do processo do aplicativo, o WorkManager entrega a tarefa a um despachante do sistema que garante a execução sob condições adequadas (disponibilidade de rede, carga da bateria, espaço livre). O WorkManager é adequado para sincronização de dados, envio de logs e backup.

Uma tarefa no WorkManager é uma classe que estende Worker (ou CoroutineWorker para corrotinas). Worker.doWork() executa em uma thread em segundo plano fornecida pelo WorkManager. O resultado é retornado via Result.success(), Result.retry() ou Result.failure(). As tarefas podem ser encadeadas: oneTimeWorkRequest.andThen(nextRequest).enqueue(). O WorkManager escolhe o momento ideal de execução considerando as restrições.

kotlin
// WorkManager com corrotinas
import android.content.Context
import androidx.work.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class SyncWorker(
    appContext: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        // Executando em Dispatchers.Default (por padrão)
        return withContext(Dispatchers.IO) {
            try {
                syncDataToServer()
                Result.success()
            } catch (e: Exception) {
                if (runAttemptCount < 3) Result.retry() else Result.failure()
            }
        }
    }

    private suspend fun syncDataToServer() {
        // Simulação de sincronização
        delay(1000)
    }
}

// Iniciando tarefa WorkManager com restrições
fun scheduleSync(context: Context) {
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresBatteryNotLow(true)
        .build()

    val syncWork = OneTimeWorkRequestBuilder<SyncWorker>()
        .setConstraints(constraints)
        .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, java.util.concurrent.TimeUnit.SECONDS)
        .build()

    WorkManager.getInstance(context).enqueue(syncWork)
}

SyncWorker estende CoroutineWorker — uma versão do Worker que suporta corrotinas. doWork() executa em Dispatchers.Default, alternando para IO para operações de rede através de withContext. As Constraints garantem que a sincronização só seja executada quando a rede estiver disponível e a bateria não estiver abaixo de um nível baixo. BackoffCriteria com EXPONENTIAL aumenta o intervalo entre tentativas: 10, 20, 40 segundos.

PeriodicWorkRequest para tarefas periódicas em segundo plano

Para tarefas periódicas (sincronização a cada 15 minutos, envio de análises a cada hora), o WorkManager fornece PeriodicWorkRequestBuilder. O intervalo mínimo é de 15 minutos. Ao contrário do OneTimeWorkRequest, o PeriodicWorkRequest não garante o cumprimento exato do intervalo — o sistema pode agrupar várias tarefas periódicas para economizar bateria. Para intervalos precisos, use AlarmManager, mas considere as restrições do Android 12+ sobre alarmes exatos.

Erros comuns ao trabalhar com threads em segundo plano

Primeiro erro — criar uma nova Thread para cada tarefa. new Thread().start() cria uma thread nativa alocando ~1 MB para a pilha. Para 100 tarefas em paralelo, são 100 MB apenas para pilhas, mais a sobrecarga de troca de contexto. Use pools de threads: Executors.newFixedThreadPool(n) (Android) ou DispatchQueue.global() (iOS) — eles reutilizam threads, reduzindo a sobrecarga em ordens de magnitude.

Segundo erro — acessar estado mutável de várias threads em segundo plano sem sincronização. Se duas threads em segundo plano escrevem simultaneamente no mesmo ArrayList ou HashMap, ocorrem condições de corrida: ConcurrentModificationException no Android, corrupção de dados no iOS. Solução: use coleções thread-safe (ConcurrentHashMap, CopyOnWriteArrayList) ou serializa o acesso através de uma única fila (DispatchQueue serial).

Terceiro erro — tarefas em segundo plano sem gerenciamento de ciclo de vida. Iniciar uma corrotina em um escopo global sem vinculá-la ao ciclo de vida da Activity ou ViewModel causa vazamentos: a tarefa continua executando após a destruição da tela. No Android, use lifecycleScope (Activity/Fragment) ou viewModelScope (ViewModel). No iOS, use weak self em closures e cancele tarefas no deinit.

Perguntas frequentes

O que é uma Background Thread em aplicativos móveis?

Background Thread — uma thread na qual são executadas operações não relacionadas à UI: requisições de rede, leitura/escrita de arquivos, análise JSON, cálculos. Ela libera a Main Thread de trabalho pesado, mantendo a interface responsiva. No iOS, as threads em segundo plano são gerenciadas através do GCD (DispatchQueue.global); no Android, através de Executors ou Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default).

Qual a diferença entre Dispatchers.IO e Dispatchers.Default?

Dispatchers.IO é projetado para operações de E/S bloqueantes: leitura de arquivos, requisições de rede, trabalho com BD. Tem um pool de 64 threads. Dispatchers.Default é para tarefas intensivas de CPU: ordenação, filtragem, processamento de imagens. Seu pool é igual ao número de núcleos da CPU. Usar Dispatchers.Default para operações de E/S pode bloquear todos os núcleos, e Dispatchers.IO para tarefas de CPU pode criar uma quantidade excessiva de threads.

Como alternar para uma thread em segundo plano no iOS?

DispatchQueue.global(qos: .background).async { } envia um bloco para a fila global em segundo plano. Após concluir o trabalho em segundo plano, é necessário retornar à thread principal através de DispatchQueue.main.async { } para atualizar a UI. Para tarefas sequenciais em segundo plano, use OperationQueue com maxConcurrentOperationCount = 1 ou DispatchQueue(label: "serial").

Quantas threads em segundo plano podem ser criadas em um aplicativo móvel?

O número recomendado de threads em segundo plano é igual ao número de núcleos da CPU mais 1 para tarefas de E/S. Em um dispositivo moderno de 8 núcleos, são 9 threads. Criar centenas de threads leva ao thread starvation: o SO gasta mais tempo na troca de contexto do que na execução de tarefas. O GCD no iOS e os Executors no Android otimizam automaticamente o pool de threads para o dispositivo atual.

É necessário retornar à Main Thread após uma corrotina?

Em Kotlin Coroutines, o retorno à Main Thread ocorre automaticamente se a corrotina foi iniciada em um escopo Main (lifecycleScope.launch, viewModelScope.launch). A função withContext(Dispatchers.IO) suspende a corrotina em uma thread IO e, após a conclusão, retoma automaticamente no dispatcher onde foi iniciada (geralmente Main). Não é necessária uma chamada explícita a DispatchQueue.main.async.

Resumo

  • Background Thread — uma thread para operações que não devem ser executadas na Main Thread: rede, arquivos, análise JSON, cálculos
  • iOS: DispatchQueue.global(qos:) e OperationQueue são as principais APIs para tarefas em segundo plano com suporte a QoS
  • Android: Executors, HandlerThread, WorkManager para Java; Dispatchers.IO/Default + corrotinas para Kotlin
  • Corrotinas com withContext alternam de thread sem callbacks e sem bloqueio (mecanismo suspend)
  • WorkManager garante a execução de tarefas em segundo plano mesmo após reiniciar o dispositivo, respeitando restrições
  • Erros: criar novas Threads em vez de usar um pool, condições de corrida ao acessar estado mutável, vazamentos por falta de vinculação ao ciclo de vida
  • Resultado da thread em segundo plano é sempre retornado à Main Thread: via Dispatchers.Main (Android) ou DispatchQueue.main.async (iOS)

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também