Background Thread mobil inkişafda: nədir, tapşırıqlar və istifadə üsulları

Müəllif: IT Sectr Dərc olunub: 2026-03-15 Oxuma vaxtı: 11 dəq

Background Thread — istifadəçi interfeysi ilə əlaqəli olmayan, uzunmüddətli əməliyyatlar üçün nəzərdə tutulmuş icra axını: şəbəkə sorğuları, fayllarla iş, JSON pars etmə, şəkil sıxlaşdırma, şifrələmə və verilənlər bazası sorğuları. iOS-da fon axınları GCD (DispatchQueue.global) və OperationQueue vasitəsilə idarə olunur, Android-də — Executors, WorkManager və Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default) ilə. Apple DispatchQueue Documentation-ya görə, fon əməliyyatı tamamlandıqdan sonra nəticə interfeysi yeniləmək üçün Main Thread-a qaytarılmalıdır.

Əsas məqamlar

  • Background Thread UI-ni bloklayan əməliyyatları yerinə yetirir: şəbəkə, fayllar, JSON, hesablamalar
  • iOS: DispatchQueue.global(qos:) və OperationQueue fon tapşırıqları üçün
  • Android: Dispatchers.IO (şəbəkə/fayllar), Dispatchers.Default (hesablamalar), WorkManager (fon tapşırıqları)
  • Korutinlər — fon işinin müasir standartı: withContext(Dispatchers.IO) axını callback cəhənnəmi olmadan dəyişir
  • Nəticə Background Thread-dən həmişə UI yeniləməsi üçün Main Thread-a qayıdır

Background Thread nədir

Background Thread — tətbiqdə Main Thread olmayan və UI-ə girişi olmayan istənilən axın. Onun vəzifəsi əsas axını ağır əməliyyatlardan azad etməkdir ki, interfeys cavabdeh qalsın. Əməliyyat sistemi fon axınlarını prosessor nüvələri arasında bölüşdürərək bir neçə tapşırığın paralel yerinə yetirilməsinə imkan verir. iOS avtomatik olaraq GCD vasitəsilə axın hovuzunu idarə edir, Android — Java Executors hovuzları vasitəsilə.

Main Thread-dən fərqli olaraq, hadisələri ardıcıl emal edən (bir-birinin ardınca), fon axınları paralel yerinə yetirilə bilər, yalnız CPU nüvələrinin sayı ilə məhdudlaşır. Məsələn, 8 nüvəli cihazda əhəmiyyətli yavaşlama olmadan 8-ə qədər paralel fon tapşırığı işə salmaq olar. Bununla belə, həddindən artıq çox axın (yüzlərlə) thread starvation-a gətirib çıxarır — nüvələr üçün rəqabət və kontekst dəyişmə (context switch) üçün əlavə xərclərin artması.

Quality of Service (QoS) — iOS mexanizmi, fon tapşırığının prioritetini təyin etməyə imkan verir. Dəyərlər: .userInteractive (ən yüksək, demək olar Main Thread), .userInitiated (istifadəçi nəticə gözləyir), .default (standart), .utility (istifadəçi birbaşa gözləmir), .background (ən aşağı, sinxronizasiya və indeksləşdirmə üçün). Android-də analoq Thread.setPriority() 1-dən 10-a qədərdir, lakin Android həmçinin axın prioritetlərini qrup şəklində idarə etmək üçün cgroups istifadə edir.

iOS-da Background Thread: GCD və DispatchQueue.global

DispatchQueue.global(qos:) — iOS-da fon növbəsi əldə etməyin əsas yolu. GCD (Grand Central Dispatch) avtomatik olaraq axın hovuzu yaradır və tapşırıqları nüvələr arasında bölüşdürür. DispatchQueue.global(qos: .background).async {} çağırışı icra blokunu ən aşağı prioritetli fon növbəsinə göndərir. Nəticəsi dərhal lazım olan tapşırıqlar üçün .userInitiated və ya .utility istifadə edin.

OperationQueue — GCD üzərində daha yüksək səviyyəli abstraksiya, əməliyyatlar arasında asılılıqları, eyni vaxtda yerinə yetirilən əməliyyatların maksimum sayını (maxConcurrentOperationCount) və prioritetləri təyin etməyə imkan verir. OperationQueue mürəkkəb çox tapşırıqlı zəncirlər üçün əlverişlidir: faylı endir -> aç -> keşdə saxla. Susmaya görə, OperationQueue başqa cür göstərilməyibsə, fon axınlarından istifadə edir.

swift
import UIKit

class ImageDownloader {

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

        // OperationQueue maxConcurrentOperationCount = 2 ilə
        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("Yükləndi: \(url.lastPathComponent)")
                }
            }
        }
    }

    // GCD: müxtəlif QoS ilə qlobal fon növbəsi
    func backgroundTaskWithQoS() {
        DispatchQueue.global(qos: .userInitiated).async {
            // Yüksək prioritet — istifadəçi nəticə gözləyir
            let result = self.heavyComputation()
            DispatchQueue.main.async {
                self.showResult(result)
            }
        }
    }

    private func heavyComputation() -> String {
        Thread.sleep(forTimeInterval: 2) // işin simulyasiyası
        return "Hesablamaların nəticəsi"
    }

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

Nümunədə OperationQueue iki şəkli paralel yükləyir (maxConcurrentOperationCount = 2) fon ilə qualityOfService = .utility vasitəsilə. GCD metodu backgroundTaskWithQoS istifadəçinin nəticəsini gözlədiyi tapşırıq üçün .userInitiated ilə qlobal növbədən istifadə edir. Hər iki yanaşma UI yeniləməsi üçün DispatchQueue.main-ə qayıtmaqla bitir — bu iOS-un məcburi tələbidir.

Serial vs Concurrent fon növbələri

GCD iki növ növbə dəstəkləyir: serial (ardıcıl) və concurrent (paralel). Serial növbələr tapşırıqları bir-bir yerinə yetirir — bu, bloklamalar olmadan ümumi resursa (fayl, VerilənlərBazası) giriş üçün əlverişlidir. Concurrent növbələr tapşırıqları paralel yerinə yetirir, onları boş nüvələrə paylayır. DispatchQueue.global — həmişə concurrent. Serial növbə yaratmaq üçün DispatchQueue(label: "com.app.queue") istifadə edin.

Android-də Background Thread: Executors və Dispatchers

Android fon axınları üçün bir neçə abstraksiya səviyyəsi təqdim edir. Klassik yanaşma — java.util.concurrent.Executors.newFixedThreadPool(n) və ya Executors.newCachedThreadPool(). Müasir — Kotlin Coroutines Dispatchers.IO (giriş-çıxış üçün: şəbəkə, fayllar, VerilənlərBazası) və Dispatchers.Default (CPU intensiv tapşırıqlar üçün: çeşidləmə, şəkil emalı). WorkManager — təxirə salınmış və zəmanətli fon tapşırıqları üçün.

HandlerThread — öz Looper (mesaj növbəsi) ilə fon axını yaratmaq üçün xüsusi Android sinfi. Executors-dan fərqli olaraq, HandlerThread Handler vasitəsilə mesajlar və Runnable göndərməyə imkan verir. Növbəyə qoyma tələb edən əməliyyatlar üçün istifadə olunur (məsələn, verilənlər bazasına ardıcıl yazma). İstifadədən sonra resursları boşaltmaq üçün HandlerThread-də quit() və ya quitSafely() çağırılmalıdır.

kotlin
// Android: Executors və Coroutines Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors

class DataRepository {

    private val ioExecutor = Executors.newFixedThreadPool(4)

    // Executors vasitəsilə klassik yanaşma
    fun loadDataLegacy(callback: (String) -> Unit) {
        ioExecutor.execute {
            val result = readFromFile()
            val handler = android.os.Handler(android.os.Looper.getMainLooper())
            handler.post { callback(result) }
        }
    }

    // Coroutines vasitəsilə müasir yanaşma
    suspend fun loadDataCoroutines(): String {
        return withContext(Dispatchers.IO) {
            // Fayl əməliyyatı — fon hovuzunda yerinə yetirilir
            readFromFile()
        }
        // Nəticə avtomatik olaraq Dispatchers.Main-ə qayıdır
    }

    // Dispatchers.Default-da CPU intensiv tapşırıq
    suspend fun processImage(pixels: IntArray): IntArray {
        return withContext(Dispatchers.Default) {
            // Çeşidləmə, filtrləmə — Default hovuzunda yerinə yetirilir
            pixels.sortedArray()
        }
    }

    private fun readFromFile(): String {
        Thread.sleep(1000) // fayldan oxuma simulyasiyası
        return "file_content"
    }

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

DataRepository nümunəsi Android-də fon axınlarının təkamülünü göstərir. Legacy metod loadDataLegacy Main Thread-a qayıtmaq üçün Executors.newFixedThreadPool(4) ilə Handler istifadə edir. Müasir loadDataCoroutines withContext(Dispatchers.IO) istifadə edir — korutin iş zamanı dayanır, axını bloklamır və avtomatik olaraq Main Thread-da bərpa olunur. Dispatchers.Default CPU-bound əməliyyatları üçün tövsiyə olunur (çeşidləmə, filtrləmə, məlumat transformasiyası).

Korutinlər fon tapşırıqlarının müasir standartı kimi

Kotlin Coroutines — sadəcə axınlarla iş üsulu deyil, prinsipial olaraq fərqli model: asinxron tapşırıqlar konkret axına bağlı deyil və bloklamadan dayana bilər (suspend). Bu o deməkdir ki, fon işi zamanı korutin axını tutmur, onu digər tapşırıqlar üçün boşaldır. Suspension mexanizmi thread starvation olmadan 4-8 axın hovuzunda yüz minlərlə concurrent tapşırığı yerinə yetirməyə imkan verir.

Üç əsas dispatcher: Dispatchers.Main (UI, bir axın), Dispatchers.IO (susmaya görə 64 axın bloklayan əməliyyatlar üçün: şəbəkə, fayllar, VerilənlərBazası), Dispatchers.Default (CPU nüvələrinin sayına bərabər, intensiv hesablamalar üçün). Onları withContext vasitəsilə birləşdirərək, proqramçı callback yaratmadan axınlar arasında keçid edir. withContext — tapşırıq yerinə yetirilənə qədər idarəni qaytarmayan suspend funksiyadır.

kotlin
// Korutinlər: fon tapşırıqlarının kompozisiyası
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay

suspend fun loadUserProfile(userId: String): UserProfile =
    coroutineScope {
        // Müxtəlif mənbələrdən paralel məlumat yükləmə
        val user = async(Dispatchers.IO) { fetchUser(userId) }
        val posts = async(Dispatchers.IO) { fetchPosts(userId) }
        val avatar = async(Dispatchers.Default) {
            processAvatar(fetchAvatar(userId))
        }

        // await() — bütün tapşırıqlar tamamlanana qədər dayanır
        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 }

loadUserProfile funksiyası async vasitəsilə üç paralel fon tapşırığı işə salır. fetchUser və fetchPosts — IO-bound (şəbəkə), Dispatchers.IO-da yerinə yetirilir. processAvatar — CPU-bound (şəkil emalı), Dispatchers.Default-da yerinə yetirilir. await() bütün tapşırıqlar tamamlanana qədər korutini dayandırır. Ümumi icra müddəti üç tapşırıq arasında maksimum vaxta bərabərdir (fetchPosts üçün 500 ms), onların cəminə deyil. Bu, coroutines-in ardıcıl icra ilə müqayisədə əsas üstünlüyüdür.

Structured concurrency: sızmaların qarşısının alınması

Structured concurrency — hər bir korutinin parent scope-a malik olduğu və valideynin ləğvinin avtomatik olaraq uşaq korutinləri ləğv etdiyi prinsip. Android-də lifecycleScope Activity məhv olduqda bütün korutinləri ləğv edir. viewModelScope — ViewModel təmizləndikdə. Bu, fon tapşırıqlarının sızmasının qarşısını alır: istifadəçi ekranı bağladısa, fon korutini artıq heç kimə lazım olmayan məlumatları yükləməyə davam etməyəcək.

WorkManager: Android üçün fon tapşırıqları

WorkManager — cihaz yenidən başladıldıqdan və ya tətbiq bağlandıqdan sonra belə yerinə yetirilməli olan fon tapşırıqları üçün Android Jetpack kitabxanası. Tətbiq prosesində yaşayan Executors və korutinlərdən fərqli olaraq, WorkManager tapşırığı sistem dispatcherinə ötürür, o da uyğun şərtlər daxilində (şəbəkə mövcudluğu, batareya səviyyəsi, boş yer) icranı təmin edir. WorkManager məlumat sinxronizasiyası, logların yüklənməsi, ehtiyat nüsxələrin yaradılması üçün uyğundur.

WorkManager-də tapşırıq Worker (və ya korutinlər üçün CoroutineWorker) sinfindən miras alan sinifdir. Worker.doWork() WorkManager tərəfindən təmin edilən fon axınında yerinə yetirilir. Nəticə Result.success(), Result.retry() və ya Result.failure() vasitəsilə qaytarılır. Tapşırıqlar zəncirlərə birləşdirilə bilər: oneTimeWorkRequest.andThen(nextRequest).enqueue(). WorkManager məhdudiyyətləri (Constraints) nəzərə alaraq optimal icra vaxtını özü seçir.

kotlin
// WorkManager korutinlərlə
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 {
        // Dispatchers.Default-da yerinə yetirilir (susmaya görə)
        return withContext(Dispatchers.IO) {
            try {
                syncDataToServer()
                Result.success()
            } catch (e: Exception) {
                if (runAttemptCount < 3) Result.retry() else Result.failure()
            }
        }
    }

    private suspend fun syncDataToServer() {
        // Sinxronizasiya simulyasiyası
        delay(1000)
    }
}

// Məhdudiyyətlərlə WorkManager tapşırığının işə salınması
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 CoroutineWorker — korutinləri dəstəkləyən Worker versiyasından miras alır. doWork() Dispatchers.Default-da yerinə yetirilir, şəbəkə əməliyyatları üçün IO-ya keçid withContext vasitəsilə olur. Constraints sinxronizasiyanın yalnız şəbəkə mövcud olduqda və batareya səviyyəsi aşağıdan aşağı olmadıqda işə düşəcəyini təmin edir. EXPONENTIAL ilə BackoffCriteria təkrar cəhdlər arasında intervalı artırır: 10, 20, 40 saniyə.

PeriodicWorkRequest müntəzəm fon tapşırıqları üçün

Müntəzəm tapşırıqlar üçün (hər 15 dəqiqədən bir sinxronizasiya, saatda bir dəfə analitika göndərmə) WorkManager PeriodicWorkRequestBuilder təqdim edir. Minimal interval — 15 dəqiqə. OneTimeWorkRequest-dən fərqli olaraq, PeriodicWorkRequest intervala dəqiq riayət olunmasına zəmanət vermir — sistem batareyaya qənaət etmək üçün bir neçə dövri tapşırığı birləşdirə bilər. Dəqiq intervallar üçün AlarmManager istifadə edin, lakin Android 12+ dəqiq alarm məhdudiyyətlərini nəzərə alın.

Fon axınları ilə işdə tipik səhvlər

Birinci səhv — hər tapşırıq üçün yeni Thread yaratmaq. new Thread().start() stek üçün ~1 MB ayırma ilə native axın yaradır. 100 paralel tapşırıq üçün bu, yalnız steklər üçün 100 MB, üstəlik context switch üçün əlavə xərclər deməkdir. Axın hovuzlarından istifadə edin: Executors.newFixedThreadPool(n) (Android) və ya DispatchQueue.global() (iOS) — onlar axınları təkrar istifadə edir, overhead-i dəfələrlə azaldır.

İkinci səhv — sinxronizasiya olmadan bir neçə fon axınından mutable vəziyyətə giriş. İki fon axını eyni anda eyni ArrayList və ya HashMap-ə yazırsa, race condition yaranır: Android-də ConcurrentModificationException, iOS-da data corruption. Həll: thread-safe kolleksiyalardan (ConcurrentHashMap, CopyOnWriteArrayList) istifadə edin və ya girişi bir növbə (DispatchQueue serial) vasitəsilə serializə edin.

Üçüncü səhv — həyat dövrü idarəsi olmadan fon tapşırıqları. Korutinin Activity və ya ViewModel-in həyat dövrünə bağlanmadan global scope-da işə salınması sızmalara gətirib çıxarır: tapşırıq ekran məhv edildikdən sonra da davam edir. Android-də lifecycleScope (Activity/Fragment) və ya viewModelScope (ViewModel) istifadə edin. iOS-da — closure-larda weak self və deinit zamanı tapşırıqların ləğvi.

Tez-tez verilən suallar

Mobil tətbiqlərdə Background Thread nədir?

Background Thread — UI ilə əlaqəli olmayan əməliyyatların yerinə yetirildiyi axın: şəbəkə sorğuları, faylların oxunması/yazılması, JSON pars etmə, hesablamalar. O, Main Thread-i ağır işdən azad edərək interfeysin cavabdehliyini qoruyur. iOS-da fon axınları GCD (DispatchQueue.global) vasitəsilə, Android-də — Executors və ya Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default) ilə idarə olunur.

Dispatchers.IO Dispatchers.Default-dan nə ilə fərqlənir?

Dispatchers.IO bloklayan giriş-çıxış əməliyyatları üçün nəzərdə tutulub: faylların oxunması, şəbəkə sorğuları, verilənlər bazası ilə iş. Onun 64 axın hovuzu var. Dispatchers.Default — CPU intensiv tapşırıqlar üçün: çeşidləmə, filtrləmə, şəkil emalı. Onun hovuzu CPU nüvələrinin sayına bərabərdir. Dispatchers.Default-un IO əməliyyatları üçün istifadəsi bütün nüvələri bloklaya bilər, Dispatchers.IO-nun CPU tapşırıqları üçün istifadəsi isə həddindən artıq çox axın yarada bilər.

iOS-da fon axınına necə keçmək olar?

DispatchQueue.global(qos: .background).async { } icra blokunu qlobal fon növbəsinə göndərir. Fon işi bitdikdən sonra UI yeniləməsi üçün DispatchQueue.main.async { } vasitəsilə əsas axına qayıtmaq lazımdır. Ardıcıl fon tapşırıqları üçün OperationQueue-dən maxConcurrentOperationCount = 1 ilə və ya DispatchQueue(label: "serial") istifadə edin.

Mobil tətbiqdə neçə fon axını yaratmaq olar?

Tövsiyə olunan fon axınlarının sayı CPU nüvələrinin sayı üstəgəl 1 IO-bound tapşırıqları üçündür. Müasir 8 nüvəli cihazda bu 9 axındır. Yüzlərlə axının yaradılması thread starvation-a gətirib çıxarır: OS tapşırıqların yerinə yetirilməsindən daha çox vaxtını kontekst dəyişməyə (context switch) sərf edir. iOS-da GCD və Android-də Executors avtomatik olaraq axın hovuzunu cari cihaza uyğun optimallaşdırır.

Korutindən sonra Main Thread-a qayıtmaq lazımdırmı?

Kotlin Coroutines-də Main Thread-a qayıdış avtomatik baş verir, əgər korutin Main-scope-da işə salınıbsa (lifecycleScope.launch, viewModelScope.launch). withContext(Dispatchers.IO) funksiyası korutini IO axınında dayandırır və tamamlandıqdan sonra avtomatik olaraq onu işə salındığı dispatcher-də (adətən Main) bərpa edir. DispatchQueue.main.async-in açıq çağırışı tələb olunmur.

Nəticə

  • Background Thread — Main Thread-da yerinə yetirilməməli olan əməliyyatlar üçün axın: şəbəkə, fayllar, JSON pars etmə, hesablamalar
  • iOS: DispatchQueue.global(qos:) və OperationQueue — QoS dəstəyi ilə fon tapşırıqları üçün əsas API
  • Android: Java üçün Executors, HandlerThread, WorkManager; Kotlin üçün Dispatchers.IO/Default + korutinlər
  • Korutinlər withContext ilə axını callback olmadan və axını bloklamadan dəyişir (suspend mexanizmi)
  • WorkManager məhdudiyyətlər nəzərə alınmaqla cihaz yenidən başladıldıqdan sonra belə fon tapşırıqlarının yerinə yetirilməsinə zəmanət verir
  • Səhvlər: hovuz yerinə yeni Thread yaratmaq, mutable vəziyyətə girişdə race condition, lifecycle-a bağlanmama səbəbindən sızmalar
  • Nəticə fon axınından həmişə Main Thread-a qayıdır: Dispatchers.Main (Android) və ya DispatchQueue.main.async (iOS) vasitəsilə

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun