Background Thread — foydalanuvchi interfeysi bilan bog'liq bo'lmagan, uzoq muddatli operatsiyalar uchun mo'ljallangan bajarish oqimi: tarmoq so'rovlari, fayllar bilan ishlash, JSON pars qilish, tasvir siqish, shifrlash va ma'lumotlar bazasi so'rovlari. iOS-da fon oqimlari GCD (DispatchQueue.global) va OperationQueue orqali boshqariladi, Android-da — Executors, WorkManager va Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default) orqali. Apple DispatchQueue Documentation ma'lumotlariga ko'ra, fon operatsiyasi tugagandan so'ng natija interfeysni yangilash uchun Main Thread-ga qaytarilishi kerak.
Asosiy ma'lumotlar
Background Thread — ilovada Main Thread bo'lmagan va UI-ga kirish imkoniga ega bo'lmagan har qanday oqim. Uning vazifasi asosiy oqimni og'ir operatsiyalardan ozod qilishdir, shunda interfeys javob beruvchan bo'lib qoladi. Operatsion tizim fon oqimlarini protsessor yadrolari bo'ylab taqsimlab, bir nechta vazifalarni parallel bajarishga imkon beradi. iOS avtomatik ravishda GCD orqali oqimlar hovuzini boshqaradi, Android — Java Executors hovuzlari orqali.
Main Thread-dan farqli o'laroq, voqealarni ketma-ket qayta ishlaydigan (birma-bir), fon oqimlari parallel bajarilishi mumkin, faqat CPU yadrolari soni bilan cheklangan. Masalan, 8 yadroli qurilmada sezilarli sekinlashuvsiz 8 tagacha parallel fon vazifasini ishga tushirish mumkin. Biroq, haddan tashqari ko'p oqimlar (yuzlab) thread starvation-ga olib keladi — yadrolar uchun raqobat va kontekst almashtirish (context switch) uchun qo'shimcha xarajatlarning oshishi.
Quality of Service (QoS) — iOS mexanizmi, fon vazifasining ustuvorligini belgilashga imkon beradi. Qiymatlar: .userInteractive (eng yuqori, deyarli Main Thread), .userInitiated (foydalanuvchi natijani kutadi), .default (standart), .utility (foydalanuvchi bevosita kutmaydi), .background (eng past, sinxronizatsiya va indekslash uchun). Android-da analog Thread.setPriority() 1 dan 10 gacha, ammo Android shuningdek oqim ustuvorliklarini guruhli boshqarish uchun cgroups dan foydalanadi.
DispatchQueue.global(qos:) — iOS-da fon navbatini olishning asosiy usuli. GCD (Grand Central Dispatch) avtomatik ravishda oqimlar hovuzini yaratadi va vazifalarni yadrolar bo'ylab taqsimlaydi. DispatchQueue.global(qos: .background).async {} chaqiruvi bajarish blokini eng past ustuvorlikdagi fon navbatiga yuboradi. Natijasi darhol kerak bo'lgan vazifalar uchun .userInitiated yoki .utility dan foydalaning.
OperationQueue — GCD ustidagi yuqori darajadagi abstraksiya bo'lib, operatsiyalar o'rtasidagi bog'liqliklarni, bir vaqtda bajariladigan operatsiyalarning maksimal sonini (maxConcurrentOperationCount) va ustuvorliklarni belgilashga imkon beradi. OperationQueue murakkab ko'p vazifali zanjirlar uchun qulay: faylni yuklab olish -> ochish -> keshda saqlash. Odatiy bo'lib, OperationQueue boshqacha ko'rsatilmagan bo'lsa, fon oqimlaridan foydalanadi.
import UIKit
class ImageDownloader {
func downloadImagesSequentially() {
let urls = ["https://example.com/1.png", "https://example.com/2.png"]
// OperationQueue maxConcurrentOperationCount = 2 bilan
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("Yuklandi: \(url.lastPathComponent)")
}
}
}
}
// GCD: turli QoS bilan global fon navbati
func backgroundTaskWithQoS() {
DispatchQueue.global(qos: .userInitiated).async {
// Yuqori ustuvorlik — foydalanuvchi natijani kutadi
let result = self.heavyComputation()
DispatchQueue.main.async {
self.showResult(result)
}
}
}
private func heavyComputation() -> String {
Thread.sleep(forTimeInterval: 2) // ish simulyatsiyasi
return "Hisoblash natijasi"
}
private func showResult(_ result: String) {
print("Result on Main: \(result)")
}
}
Misolda OperationQueue ikkita rasmni parallel yuklaydi (maxConcurrentOperationCount = 2) fon bilan qualityOfService = .utility orqali. GCD metodi backgroundTaskWithQoS foydalanuvchi natijasini kutayotgan vazifa uchun .userInitiated bilan global navbatdan foydalanadi. Ikkala yondashuv UI yangilash uchun DispatchQueue.main-ga qaytish bilan tugaydi — bu iOS-ning majburiy talabidir.
GCD ikki turdagi navbatni qo'llab-quvvatlaydi: serial (ketma-ket) va concurrent (parallel). Serial navbatlar vazifalarni birma-bir bajaradi — bu blokirovkalarsiz umumiy resursga (fayl, Ma'lumotlarBazasi) kirish uchun qulay. Concurrent navbatlar vazifalarni parallel bajaradi, ularni bo'sh yadrolarga taqsimlaydi. DispatchQueue.global — har doim concurrent. Serial navbat yaratish uchun DispatchQueue(label: "com.app.queue") dan foydalaning.
Android fon oqimlari uchun bir nechta abstraksiya darajalarini taqdim etadi. Klassik yondashuv — java.util.concurrent.Executors.newFixedThreadPool(n) yoki Executors.newCachedThreadPool(). Zamonaviy — Kotlin Coroutines Dispatchers.IO (kirish-chiqish uchun: tarmoq, fayllar, Ma'lumotlarBazasi) va Dispatchers.Default (CPU intensiv vazifalar uchun: saralash, tasvir qayta ishlash). WorkManager — kechiktirilgan va kafolatlangan fon vazifalari uchun.
HandlerThread — o'z Looper (xabar navbati) bilan fon oqimi yaratish uchun maxsus Android klassi. Executors-dan farqli o'laroq, HandlerThread Handler orqali xabarlar va Runnable yuborishga imkon beradi. Navbatga qo'yishni talab qiladigan operatsiyalar uchun ishlatiladi (masalan, ma'lumotlar bazasiga ketma-ket yozish). Foydalanishdan keyin resurslarni bo'shatish uchun HandlerThread-da quit() yoki quitSafely() chaqirilishi kerak.
// Android: Executors va Coroutines Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors
class DataRepository {
private val ioExecutor = Executors.newFixedThreadPool(4)
// Executors orqali klassik yondashuv
fun loadDataLegacy(callback: (String) -> Unit) {
ioExecutor.execute {
val result = readFromFile()
val handler = android.os.Handler(android.os.Looper.getMainLooper())
handler.post { callback(result) }
}
}
// Coroutines orqali zamonaviy yondashuv
suspend fun loadDataCoroutines(): String {
return withContext(Dispatchers.IO) {
// Fayl operatsiyasi — fon hovuzida bajariladi
readFromFile()
}
// Natija avtomatik ravishda Dispatchers.Main-ga qaytadi
}
// Dispatchers.Default da CPU intensiv vazifa
suspend fun processImage(pixels: IntArray): IntArray {
return withContext(Dispatchers.Default) {
// Saralash, filtrlash — Default hovuzida bajariladi
pixels.sortedArray()
}
}
private fun readFromFile(): String {
Thread.sleep(1000) // fayldan o'qish simulyatsiyasi
return "file_content"
}
fun cleanup() {
ioExecutor.shutdown()
}
}
DataRepository misoli Android-da fon oqimlarining evolyutsiyasini ko'rsatadi. Legacy metod loadDataLegacy Main Thread-ga qaytish uchun Executors.newFixedThreadPool(4) bilan Handler dan foydalanadi. Zamonaviy loadDataCoroutines withContext(Dispatchers.IO) dan foydalanadi — korutin ish vaqtida to'xtaydi, oqimni bloklamaydi va avtomatik ravishda Main Thread-da tiklanadi. Dispatchers.Default CPU-bound operatsiyalari uchun tavsiya etiladi (saralash, filtrlash, ma'lumotlarni transformatsiya qilish).
Kotlin Coroutines — shunchaki oqimlar bilan ishlash usuli emas, balki tubdan farqli model: asinxron vazifalar muayyan oqimga bog'liq emas va bloklamasdan to'xtab qolishi mumkin (suspend). Bu shuni anglatadiki, fon ishi vaqtida korutin oqimni egallab olmaydi, balki uni boshqa vazifalar uchun bo'shatadi. Suspension mexanizmi thread starvation holda 4-8 oqim hovuzida yuz minglab concurrent vazifalarni bajarishga imkon beradi.
Uchta asosiy dispatcher: Dispatchers.Main (UI, bitta oqim), Dispatchers.IO (odatiy bo'lib 64 oqim bloklovchi operatsiyalar uchun: tarmoq, fayllar, Ma'lumotlarBazasi), Dispatchers.Default (CPU yadrolari soniga teng, intensiv hisoblashlar uchun). Ularni withContext orqali birlashtirib, dasturchi callback yaratmasdan oqimlar o'rtasida o'tadi. withContext — vazifa bajarilmaguncha boshqaruvni qaytarmaydigan suspend funksiyadir.
// Korutinlar: fon vazifalari kompozitsiyasi
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
suspend fun loadUserProfile(userId: String): UserProfile =
coroutineScope {
// Turli manbalardan parallel ma'lumot yuklash
val user = async(Dispatchers.IO) { fetchUser(userId) }
val posts = async(Dispatchers.IO) { fetchPosts(userId) }
val avatar = async(Dispatchers.Default) {
processAvatar(fetchAvatar(userId))
}
// await() — barcha vazifalar tugaguncha to'xtaydi
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 funksiyasi async orqali uchta parallel fon vazifasini ishga tushiradi. fetchUser va fetchPosts — IO-bound (tarmoq), Dispatchers.IO da bajariladi. processAvatar — CPU-bound (tasvirni qayta ishlash), Dispatchers.Default da bajariladi. await() barcha vazifalar tugaguncha korutinni to'xtatadi. Umumiy bajarish vaqti uchta vazifa orasidagi maksimal vaqtga teng (fetchPosts uchun 500 ms), ularning yig'indisiga emas. Bu coroutines-ning ketma-ket bajarishga nisbatan asosiy ustunligidir.
Structured concurrency — har bir korutin ota-scope ga ega bo'lgan va ota-ona bekor qilinishi avtomatik ravishda bolakay korutinlarni bekor qiladigan prinsip. Android-da lifecycleScope Activity yo'q qilinganda barcha korutinlarni bekor qiladi. viewModelScope — ViewModel tozalanganda. Bu fon vazifalarining oqishining oldini oladi: agar foydalanuvchi ekranni yopgan bo'lsa, fon korutini endi hech kimga kerak bo'lmagan ma'lumotlarni yuklashni davom ettirmaydi.
WorkManager — qurilma qayta yoqilgandan yoki ilova yopilgandan keyin ham bajarilishi kerak bo'lgan fon vazifalari uchun Android Jetpack kutubxonasi. Ilova jarayonida yashovchi Executors va korutinlardan farqli o'laroq, WorkManager vazifani tizim dispetcheriga topshiradi, u tegishli sharoitlarda (tarmoq mavjudligi, batareya darajasi, bo'sh joy) bajarilishni kafolatlaydi. WorkManager ma'lumotlarni sinxronlashtirish, loglarni yuklash, zaxira nusxalarni yaratish uchun mos keladi.
WorkManager-dagi vazifa Worker (yoki korutinlar uchun CoroutineWorker) sinfidan meros oladigan sinfdir. Worker.doWork() WorkManager tomonidan ta'minlangan fon oqimida bajariladi. Natija Result.success(), Result.retry() yoki Result.failure() orqali qaytariladi. Vazifalar zanjirlarga birlashtirilishi mumkin: oneTimeWorkRequest.andThen(nextRequest).enqueue(). WorkManager cheklovlarni (Constraints) hisobga olgan holda optimal bajarish vaqtini o'zi tanlaydi.
// WorkManager korutinlar bilan
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 bajariladi (odatiy)
return withContext(Dispatchers.IO) {
try {
syncDataToServer()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
private suspend fun syncDataToServer() {
// Sinxronizatsiya simulyatsiyasi
delay(1000)
}
}
// Cheklovlar bilan WorkManager vazifasini ishga tushirish
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 — korutinlarni qo'llab-quvvatlaydigan Worker versiyasidan meros oladi. doWork() Dispatchers.Default da bajariladi, tarmoq operatsiyalari uchun IO ga o'tish withContext orqali amalga oshiriladi. Constraints sinxronizatsiya faqat tarmoq mavjud bo'lganda va batareya darajasi pastdan past bo'lmaganda ishga tushishini kafolatlaydi. EXPONENTIAL bilan BackoffCriteria takroriy urinishlar orasidagi intervalni oshiradi: 10, 20, 40 soniya.
Muntazam vazifalar uchun (har 15 daqiqada sinxronizatsiya, soatda bir marta analitika yuborish) WorkManager PeriodicWorkRequestBuilder ni taqdim etadi. Minimal interval — 15 daqiqa. OneTimeWorkRequest-dan farqli o'laroq, PeriodicWorkRequest intervalga aniq rioya qilinishini kafolatlamaydi — tizim batareyani tejash uchun bir nechta davriy vazifalarni birlashtirishi mumkin. Aniq intervallar uchun AlarmManager dan foydalaning, lekin Android 12+ aniq signal cheklovlarini hisobga oling.
Birinchi xato — har bir vazifa uchun yangi Thread yaratish. new Thread().start() stek uchun ~1 MB ajratish bilan native oqim yaratadi. 100 parallel vazifa uchun bu faqat steklar uchun 100 MB, qo'shimcha context switch xarajatlari bilan. Oqimlar hovuzlaridan foydalaning: Executors.newFixedThreadPool(n) (Android) yoki DispatchQueue.global() (iOS) — ular oqimlarni qayta ishlatadi, overhead-ni o'nlab marta kamaytiradi.
Ikkinchi xato — sinxronizatsiyasiz bir nechta fon oqimlaridan mutable holatga kirish. Ikki fon oqimi bir vaqtda bir xil ArrayList yoki HashMap-ga yozsa, race condition yuzaga keladi: Android-da ConcurrentModificationException, iOS-da ma'lumotlar buzilishi. Yechim: thread-safe to'plamlardan (ConcurrentHashMap, CopyOnWriteArrayList) foydalaning yoki kirishni bitta navbat (DispatchQueue serial) orqali seriyalizatsiya qiling.
Uchinchi xato — hayotiy tsiklni boshqarmasdan fon vazifalari. Korutinni Activity yoki ViewModel ning hayotiy tsikliga bog'lamasdan global scope-da ishga tushirish oqishlarga olib keladi: vazifa ekran yo'q qilingandan keyin ham davom etadi. Android-da lifecycleScope (Activity/Fragment) yoki viewModelScope (ViewModel) dan foydalaning. iOS-da — closure-larda weak self va deinit vaqtida vazifalarni bekor qilish.
Ko'p beriladigan savollar
Background Thread — UI bilan bog'liq bo'lmagan operatsiyalar bajariladigan oqim: tarmoq so'rovlari, fayllarni o'qish/yozish, JSON pars qilish, hisoblashlar. U Main Thread-ni og'ir ishdan ozod qilib, interfeysning javob beruvchanligini saqlaydi. iOS-da fon oqimlari GCD (DispatchQueue.global) orqali, Android-da — Executors yoki Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default) orqali boshqariladi.
Dispatchers.IO bloklovchi kirish-chiqish operatsiyalari uchun mo'ljallangan: fayllarni o'qish, tarmoq so'rovlari, ma'lumotlar bazasi bilan ishlash. Uning 64 oqim hovuzi bor. Dispatchers.Default — CPU intensiv vazifalar uchun: saralash, filtrlash, tasvirni qayta ishlash. Uning hovuzi CPU yadrolari soniga teng. Dispatchers.Default ni IO operatsiyalari uchun ishlatish barcha yadrolarni bloklashi mumkin, Dispatchers.IO ni CPU vazifalari uchun ishlatish esa haddan tashqari ko'p oqim yaratishi mumkin.
DispatchQueue.global(qos: .background).async { } bajarish blokini global fon navbatiga yuboradi. Fon ishi tugagandan so'ng UI yangilash uchun DispatchQueue.main.async { } orqali asosiy oqimga qaytish kerak. Ketma-ket fon vazifalari uchun OperationQueue dan maxConcurrentOperationCount = 1 bilan yoki DispatchQueue(label: "serial") dan foydalaning.
Tavsiya etilgan fon oqimlari soni CPU yadrolari soni plyus 1 IO-bound vazifalari uchun. Zamonaviy 8 yadroli qurilmada bu 9 oqim. Yuzlab oqimlarni yaratish thread starvation-ga olib keladi: OT vazifalarni bajarishdan ko'ra ko'proq vaqtini kontekst almashtirishga (context switch) sarflaydi. iOS-da GCD va Android-da Executors avtomatik ravishda oqim hovuzini joriy qurilmaga moslashtiradi.
Kotlin Coroutines-da Main Thread-ga qaytish avtomatik ravishda sodir bo'ladi, agar korutin Main-scope-da ishga tushirilgan bo'lsa (lifecycleScope.launch, viewModelScope.launch). withContext(Dispatchers.IO) funksiyasi korutinni IO oqimida to'xtatadi va tugagandan so'ng avtomatik ravishda uni ishga tushirilgan dispatcher-da (odatda Main) tiklaydi. DispatchQueue.main.async ni aniq chaqirish talab qilinmaydi.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.