Background Thread în dezvoltarea mobilă: ce este, sarcini și metode de utilizare

Autor: IT Sectr Publicat: 2026-03-15 Timp de citire: 11 min

Background Thread — un fir de execuție neasociat cu interfața de utilizator, destinat operațiunilor lungi: cereri de rețea, lucru cu fișiere, parsare JSON, comprimare imagini, criptare și cereri la baza de date. În iOS, firele de fundal sunt gestionate prin GCD (DispatchQueue.global) și OperationQueue, în Android — prin Executors, WorkManager și Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default). Conform Apple DispatchQueue Documentation, după finalizarea operațiunii de fundal, rezultatul trebuie returnat pe Main Thread pentru actualizarea interfeței.

Puncte principale

  • Background Thread execută operațiuni care blochează UI: rețea, fișiere, JSON, calcule
  • iOS: DispatchQueue.global(qos:) și OperationQueue pentru sarcini de fundal
  • Android: Dispatchers.IO (rețea/fișiere), Dispatchers.Default (calcule), WorkManager (sarcini de fundal)
  • Corutinele — standardul modern pentru munca de fundal: withContext(Dispatchers.IO) comută firul fără iadul callback-urilor
  • Rezultatul din Background Thread se returnează întotdeauna pe Main Thread pentru actualizarea UI

Ce este Background Thread

Background Thread — orice fir în aplicație care nu este Main Thread și nu are acces la UI. Sarcina sa este să elibereze firul principal de operațiunile grele, astfel încât interfața să rămână receptivă. Sistemul de operare distribuie firele de fundal pe nucleele procesorului, permițând executarea paralelă a mai multor sarcini. iOS gestionează automat pool-ul de fire prin GCD, Android — prin pool-urile Java Executors.

Spre deosebire de Main Thread, care procesează evenimente secvențial (unul după altul), firele de fundal pot fi executate în paralel, limitate doar de numărul de nuclee CPU. De exemplu, pe un dispozitiv cu 8 nuclee se pot lansa până la 8 sarcini paralele de fundal fără o încetinire semnificativă. Cu toate acestea, un număr excesiv de fire (sute) duce la thread starvation — concurență pentru nuclee și creșterea costurilor de schimbare a contextului (context switch).

Quality of Service (QoS) — mecanism iOS care permite specificarea priorității sarcinii de fundal. Valori: .userInteractive (cel mai înalt, aproape Main Thread), .userInitiated (utilizatorul așteaptă rezultatul), .default (standard), .utility (utilizatorul nu așteaptă direct), .background (cel mai scăzut, pentru sincronizare și indexare). În Android, analogul este Thread.setPriority() de la 1 la 10, dar Android folosește și cgroups pentru gestionarea în grup a priorităților firelor.

Background Thread în iOS: GCD și DispatchQueue.global

DispatchQueue.global(qos:) — modul principal de a obține o coadă de fundal în iOS. GCD (Grand Central Dispatch) creează automat un pool de fire și distribuie sarcinile pe nuclee. Apelul DispatchQueue.global(qos: .background).async {} trimite un bloc de execuție în coada de fundal cu cea mai mică prioritate. Pentru sarcinile al căror rezultat este necesar imediat, utilizați .userInitiated sau .utility.

OperationQueue — o abstractizare de nivel superior peste GCD, care permite specificarea dependențelor între operațiuni, numărul maxim de operațiuni executate simultan (maxConcurrentOperationCount) și prioritățile. OperationQueue este convenabilă pentru lanțuri complexe multitasking: descarcă fișier -> despachetează -> salvează în cache. În mod implicit, OperationQueue folosește fire de fundal, dacă nu se specifică altfel.

swift
import UIKit

class ImageDownloader {

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

        // OperationQueue cu 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("Încărcat: \(url.lastPathComponent)")
                }
            }
        }
    }

    // GCD: coadă globală de fundal cu diferite QoS
    func backgroundTaskWithQoS() {
        DispatchQueue.global(qos: .userInitiated).async {
            // Prioritate ridicată — utilizatorul așteaptă rezultatul
            let result = self.heavyComputation()
            DispatchQueue.main.async {
                self.showResult(result)
            }
        }
    }

    private func heavyComputation() -> String {
        Thread.sleep(forTimeInterval: 2) // simulare muncă
        return "Rezultatul calculelor"
    }

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

În exemplu, OperationQueue încarcă două imagini în paralel (maxConcurrentOperationCount = 2) cu fundal prin qualityOfService = .utility. Metoda GCD backgroundTaskWithQoS folosește coada globală cu .userInitiated pentru sarcina al cărei rezultat utilizatorul îl așteaptă. Ambele abordări se încheie cu revenirea pe DispatchQueue.main pentru actualizarea UI — aceasta este o cerință obligatorie iOS.

Cozile de fundal Serial vs Concurrent

GCD suportă două tipuri de cozi: serial (secvențiale) și concurrent (paralele). Cozile serial execută sarcinile una după alta — acest lucru este convenabil pentru accesul la o resursă partajată (fișier, BazăDeDate) fără blocări. Cozile concurrent execută sarcinile în paralel, distribuindu-le pe nucleele libere. DispatchQueue.global — este întotdeauna concurrent. Pentru a crea o coadă serial, utilizați DispatchQueue(label: "com.app.queue").

Background Thread în Android: Executors și Dispatchers

Android oferă mai multe niveluri de abstractizare pentru firele de fundal. Abordarea clasică — java.util.concurrent.Executors.newFixedThreadPool(n) sau Executors.newCachedThreadPool(). Abordarea modernă — Kotlin Coroutines cu Dispatchers.IO (pentru intrare-ieșire: rețea, fișiere, BazăDeDate) și Dispatchers.Default (pentru sarcini intensive CPU: sortare, procesare imagini). WorkManager — pentru sarcini de fundal amânate și garantate.

HandlerThread — o clasă specială Android pentru crearea unui fir de fundal cu propriul Looper (coadă de mesaje). Spre deosebire de Executors, HandlerThread permite trimiterea de mesaje și Runnable prin Handler. Este utilizat pentru operațiuni care necesită punerea în coadă (de exemplu, scrierea secvențială în baza de date). După utilizare, trebuie apelat quit() sau quitSafely() pentru eliberarea resurselor.

kotlin
// Android: Executors și Coroutines Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors

class DataRepository {

    private val ioExecutor = Executors.newFixedThreadPool(4)

    // Abordare clasică prin Executors
    fun loadDataLegacy(callback: (String) -> Unit) {
        ioExecutor.execute {
            val result = readFromFile()
            val handler = android.os.Handler(android.os.Looper.getMainLooper())
            handler.post { callback(result) }
        }
    }

    // Abordare modernă prin Coroutines
    suspend fun loadDataCoroutines(): String {
        return withContext(Dispatchers.IO) {
            // Operațiune cu fișier — executată în pool-ul de fundal
            readFromFile()
        }
        // Rezultatul se returnează automat pe Dispatchers.Main
    }

    // Sarcină intensivă CPU pe Dispatchers.Default
    suspend fun processImage(pixels: IntArray): IntArray {
        return withContext(Dispatchers.Default) {
            // Sortare, filtrare — executată pe pool-ul Default
            pixels.sortedArray()
        }
    }

    private fun readFromFile(): String {
        Thread.sleep(1000) // simulare citire din fișier
        return "file_content"
    }

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

Exemplul DataRepository arată evoluția firelor de fundal în Android. Metoda legacy loadDataLegacy folosește Executors.newFixedThreadPool(4) cu Handler pentru revenirea pe Main Thread. Modernul loadDataCoroutines folosește withContext(Dispatchers.IO) — corutina se suspendă pe durata lucrului, fără a bloca firul, și se reia automat pe Main Thread. Dispatchers.Default este recomandat pentru operațiuni CPU-bound (sortare, filtrare, transformare date).

Corutinele ca standard modern pentru sarcini de fundal

Kotlin Coroutines — nu doar un mod de a lucra cu firele, ci un model fundamental diferit: sarcinile asincrone nu sunt legate de un fir specific și se pot suspenda (suspend) fără blocare. Aceasta înseamnă că în timpul lucrului în fundal, corutina nu ocupă un fir, ci îl eliberează pentru alte sarcini. Mecanismul suspension permite executarea a sute de mii de sarcini concurrent pe un pool de 4-8 fire fără thread starvation.

Trei dispatcheri principali: Dispatchers.Main (UI, un fir), Dispatchers.IO (64 de fire în mod implicit pentru operațiuni blocante: rețea, fișiere, BazăDeDate), Dispatchers.Default (egal cu numărul de nuclee CPU, pentru calcule intensive). Combinându-le prin withContext, dezvoltatorul comută între fire fără a crea callback-uri. withContext este o funcție suspend care nu returnează controlul până când sarcina nu este finalizată.

kotlin
// Corutine: compoziția sarcinilor de fundal
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay

suspend fun loadUserProfile(userId: String): UserProfile =
    coroutineScope {
        // Încărcare paralelă a datelor din diferite surse
        val user = async(Dispatchers.IO) { fetchUser(userId) }
        val posts = async(Dispatchers.IO) { fetchPosts(userId) }
        val avatar = async(Dispatchers.Default) {
            processAvatar(fetchAvatar(userId))
        }

        // await() — se suspendă până la finalizarea tuturor sarcinilor
        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 }

Funcția loadUserProfile lansează trei sarcini paralele de fundal prin async. fetchUser și fetchPosts — IO-bound (rețea), se execută pe Dispatchers.IO. processAvatar — CPU-bound (procesare imagine), se execută pe Dispatchers.Default. await() suspendă corutina până la finalizarea tuturor sarcinilor. Timpul total de execuție este egal cu timpul maxim dintre cele trei sarcini (500 ms pentru fetchPosts), nu suma lor. Acesta este avantajul cheie al corutinelor față de execuția secvențială.

Structured concurrency: prevenirea scurgerilor

Structured concurrency — principiul conform căruia fiecare corutină are un scope părinte, iar anularea părintelui anulează automat corutinele copil. În Android, lifecycleScope anulează toate corutinele la distrugerea Activity. viewModelScope — la curățarea ViewModel. Aceasta previne scurgerile sarcinilor de fundal: dacă utilizatorul a închis ecranul, corutina de fundal nu va continua să încarce date care nu mai sunt necesare nimănui.

WorkManager: sarcini de fundal pentru Android

WorkManager — biblioteca Android Jetpack pentru executarea sarcinilor de fundal care trebuie executate chiar și după repornirea dispozitivului sau închiderea aplicației. Spre deosebire de Executors și corutine, care trăiesc în procesul aplicației, WorkManager transferă sarcina unui dispatcher de sistem care garantează execuția în condiții potrivite (disponibilitatea rețelei, nivelul bateriei, spațiu liber). WorkManager este potrivit pentru sincronizarea datelor, încărcarea logurilor, backup.

O sarcină în WorkManager este o clasă care moștenește Worker (sau CoroutineWorker pentru corutine). Worker.doWork() se execută pe un fir de fundal furnizat de WorkManager. Rezultatul este returnat prin Result.success(), Result.retry() sau Result.failure(). Sarcinile pot fi combinate în lanțuri: oneTimeWorkRequest.andThen(nextRequest).enqueue(). WorkManager însuși alege timpul optim de execuție ținând cont de constrângeri (Constraints).

kotlin
// WorkManager cu corutine
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 {
        // Se execută pe Dispatchers.Default (implicit)
        return withContext(Dispatchers.IO) {
            try {
                syncDataToServer()
                Result.success()
            } catch (e: Exception) {
                if (runAttemptCount < 3) Result.retry() else Result.failure()
            }
        }
    }

    private suspend fun syncDataToServer() {
        // Simulare sincronizare
        delay(1000)
    }
}

// Lansare sarcină WorkManager cu constrângeri
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 moștenește CoroutineWorker — versiunea Worker care suportă corutine. doWork() se execută pe Dispatchers.Default, comutarea pe IO pentru operațiuni de rețea prin withContext. Constraints garantează că sincronizarea va porni doar când există rețea și nivelul bateriei nu este mai scăzut decât scăzut. BackoffCriteria cu EXPONENTIAL mărește intervalul între încercările repetate: 10, 20, 40 de secunde.

PeriodicWorkRequest pentru sarcini regulate de fundal

Pentru sarcini regulate (sincronizare la fiecare 15 minute, trimitere analize o dată pe oră) WorkManager oferă PeriodicWorkRequestBuilder. Intervalul minim — 15 minute. Spre deosebire de OneTimeWorkRequest, PeriodicWorkRequest nu garantează respectarea exactă a intervalului — sistemul poate combina mai multe sarcini periodice pentru economisirea bateriei. Pentru intervale precise, utilizați AlarmManager, dar țineți cont de limitările Android 12+ privind alarmele precise.

Erori tipice la lucrul cu firele de fundal

Prima eroare — crearea unui nou Thread pentru fiecare sarcină. new Thread().start() creează un fir nativ cu alocare de ~1 MB pentru stivă. Pentru 100 de sarcini paralele, aceasta înseamnă 100 MB doar pentru stive, plus costurile de context switch. Utilizați pool-uri de fire: Executors.newFixedThreadPool(n) (Android) sau DispatchQueue.global() (iOS) — acestea reutilizează firele, reducând overhead-ul de zeci de ori.

A doua eroare — accesul la starea mutable din mai multe fire de fundal fără sincronizare. Dacă două fire de fundal scriu simultan în același ArrayList sau HashMap, apar race condition: ConcurrentModificationException în Android, coruperea datelor în iOS. Soluție: utilizați colecții thread-safe (ConcurrentHashMap, CopyOnWriteArrayList) sau serializați accesul printr-o singură coadă (DispatchQueue serial).

A treia eroare — sarcini de fundal fără gestionarea ciclului de viață. Lansarea unei corutine în scope global fără legarea de ciclul de viață al Activity sau ViewModel duce la scurgeri: sarcina continuă să se execute după distrugerea ecranului. În Android, utilizați lifecycleScope (Activity/Fragment) sau viewModelScope (ViewModel). În iOS — weak self în closure-uri și anularea sarcinilor la deinit.

Întrebări frecvente

Ce este Background Thread în aplicațiile mobile?

Background Thread — firul pe care se execută operațiuni neasociate cu UI: cereri de rețea, citire/scriere fișiere, parsare JSON, calcule. Acesta eliberează Main Thread de munca grea, păstrând receptivitatea interfeței. În iOS, firele de fundal sunt gestionate prin GCD (DispatchQueue.global), în Android — prin Executors sau Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default).

Cu ce diferă Dispatchers.IO de Dispatchers.Default?

Dispatchers.IO este destinat operațiunilor blocante de intrare-ieșire: citire fișiere, cereri de rețea, lucru cu baza de date. Are un pool de 64 de fire. Dispatchers.Default — pentru sarcini intensive CPU: sortare, filtrare, procesare imagini. Pool-ul său este egal cu numărul de nuclee CPU. Utilizarea Dispatchers.Default pentru operațiuni IO poate bloca toate nucleele, iar Dispatchers.IO pentru sarcini CPU — poate crea un număr excesiv de fire.

Cum se comută pe un fir de fundal în iOS?

DispatchQueue.global(qos: .background).async { } trimite un bloc de execuție în coada globală de fundal. După finalizarea lucrului în fundal, trebuie să reveniți pe firul principal prin DispatchQueue.main.async { } pentru actualizarea UI. Pentru sarcini secvențiale de fundal, utilizați OperationQueue cu maxConcurrentOperationCount = 1 sau DispatchQueue(label: "serial").

Câte fire de fundal se pot crea într-o aplicație mobilă?

Numărul recomandat de fire de fundal este egal cu numărul de nuclee CPU plus 1 pentru sarcini IO-bound. Pe un dispozitiv modern cu 8 nuclee, aceasta înseamnă 9 fire. Crearea a sute de fire duce la thread starvation: sistemul de operare petrece mai mult timp pe schimbarea contextului (context switch) decât pe executarea sarcinilor. GCD în iOS și Executors în Android optimizează automat pool-ul de fire pentru dispozitivul curent.

Este necesară revenirea pe Main Thread după o corutină?

În Kotlin Coroutines, revenirea pe Main Thread are loc automat dacă corutina a fost lansată în Main-scope (lifecycleScope.launch, viewModelScope.launch). Funcția withContext(Dispatchers.IO) suspendă corutina pe firul IO, iar după finalizare o reia automat pe dispatcher-ul pe care a fost lansată (de obicei Main). Nu este necesară apelarea explicită a DispatchQueue.main.async.

Rezumat

  • Background Thread — fir pentru operațiuni care nu trebuie executate pe Main Thread: rețea, fișiere, parsare JSON, calcule
  • iOS: DispatchQueue.global(qos:) și OperationQueue — API principale pentru sarcini de fundal cu suport QoS
  • Android: Executors, HandlerThread, WorkManager pentru Java; Dispatchers.IO/Default + corutine pentru Kotlin
  • Corutinele cu withContext comută firul fără callback-uri și fără blocarea firului (mecanism suspend)
  • WorkManager garantează executarea sarcinilor de fundal chiar și după repornirea dispozitivului, ținând cont de constrângeri
  • Erori: crearea de Thread-uri noi în loc de pool, race condition la accesul la starea mutable, scurgeri din cauza lipsei de legare la ciclul de viață
  • Rezultatul din firul de fundal se returnează întotdeauna pe Main Thread: prin Dispatchers.Main (Android) sau DispatchQueue.main.async (iOS)

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și