Background Thread sa mobile development: ano ito, mga gawain at mga paraan ng paggamit

May-akda: IT Sectr Nai-publish: 2026-03-15 Oras ng pagbabasa: 11 min

Background Thread — isang thread ng pagpapatupad na hindi nauugnay sa user interface, na idinisenyo para sa mahabang operasyon: mga kahilingan sa network, pagtatrabaho sa mga file, JSON parsing, compression ng imahe, encryption at mga query sa database. Sa iOS, ang mga background thread ay pinamamahalaan sa pamamagitan ng GCD (DispatchQueue.global) at OperationQueue, sa Android — sa pamamagitan ng Executors, WorkManager at Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default). Ayon sa Apple DispatchQueue Documentation, pagkatapos makumpleto ang background operation, ang resulta ay dapat ibalik sa Main Thread upang i-update ang interface.

Mga pangunahing punto

  • Background Thread ay nagsasagawa ng mga operasyon na humaharang sa UI: network, mga file, JSON, mga pagkalkula
  • iOS: DispatchQueue.global(qos:) at OperationQueue para sa mga background task
  • Android: Dispatchers.IO (network/mga file), Dispatchers.Default (mga pagkalkula), WorkManager (mga background task)
  • Coroutines — modernong pamantayan ng background work: withContext(Dispatchers.IO) ay nagpapalit ng thread nang walang callback hell
  • Resulta mula sa Background Thread ay palaging ibinabalik sa Main Thread para i-update ang UI

Ano ang Background Thread

Background Thread — anumang thread sa application na hindi Main Thread at walang access sa UI. Ang gawain nito ay palayain ang pangunahing thread mula sa mabibigat na operasyon upang ang interface ay manatiling tumutugon. Ibinahagi ng operating system ang mga background thread sa mga core ng processor, na nagpapahintulot sa maraming mga gawain na maisagawa nang magkatulad. Awtomatikong pinamamahalaan ng iOS ang pool ng thread sa pamamagitan ng GCD, Android — sa pamamagitan ng Java Executors pool.

Hindi tulad ng Main Thread, na nagpoproseso ng mga kaganapan nang sunud-sunod (isa-isa), ang mga background thread ay maaaring isagawa nang magkatulad, limitado lamang sa bilang ng mga CPU core. Halimbawa, sa isang device na may 8 core, hanggang 8 parallel background task ang maaaring ilunsad nang walang makabuluhang pagbagal. Gayunpaman, ang sobrang dami ng mga thread (daan-daan) ay humahantong sa thread starvation — kompetisyon para sa mga core at pagtaas ng overhead para sa paglipat ng konteksto (context switch).

Quality of Service (QoS) — isang mekanismo ng iOS na nagpapahintulot sa iyo na tukuyin ang priyoridad ng isang background task. Mga halaga: .userInteractive (pinakamataas, halos Main Thread), .userInitiated (naghihintay ang user ng resulta), .default (standard), .utility (hindi direktang naghihintay ang user), .background (pinakamababa, para sa pag-sync at pag-index). Sa Android, ang katumbas ay Thread.setPriority() mula 1 hanggang 10, ngunit ang Android ay gumagamit din ng cgroups para sa group management ng mga priyoridad ng thread.

Background Thread sa iOS: GCD at DispatchQueue.global

DispatchQueue.global(qos:) — ang pangunahing paraan upang makakuha ng background queue sa iOS. GCD (Grand Central Dispatch) ay awtomatikong lumilikha ng pool ng thread at namamahagi ng mga gawain sa mga core. Ang tawag na DispatchQueue.global(qos: .background).async {} ay nagpapadala ng execution block sa background queue na may pinakamababang priyoridad. Para sa mga gawain na ang resulta ay kailangan kaagad, gamitin ang .userInitiated o .utility.

OperationQueue — isang mas mataas na antas ng abstraction sa ibabaw ng GCD, na nagpapahintulot sa iyo na tukuyin ang mga dependency sa pagitan ng mga operasyon, maximum na bilang ng sabay-sabay na operasyon (maxConcurrentOperationCount) at mga priyoridad. Ang OperationQueue ay maginhawa para sa mga kumplikadong multitasking chain: i-download ang file -> i-unpack -> i-save sa cache. Sa pamamagitan ng default, ang OperationQueue ay gumagamit ng mga background thread, maliban kung iba ang tinukoy.

swift
import UIKit

class ImageDownloader {

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

        // OperationQueue na may 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("Na-load: \(url.lastPathComponent)")
                }
            }
        }
    }

    // GCD: global background queue na may iba't ibang QoS
    func backgroundTaskWithQoS() {
        DispatchQueue.global(qos: .userInitiated).async {
            // Mataas na priyoridad — naghihintay ang user ng resulta
            let result = self.heavyComputation()
            DispatchQueue.main.async {
                self.showResult(result)
            }
        }
    }

    private func heavyComputation() -> String {
        Thread.sleep(forTimeInterval: 2) // simulasyon ng trabaho
        return "Resulta ng mga pagkalkula"
    }

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

Sa halimbawa, ang OperationQueue ay naglo-load ng dalawang larawan nang magkatulad (maxConcurrentOperationCount = 2) na may background sa pamamagitan ng qualityOfService = .utility. Ang GCD method na backgroundTaskWithQoS ay gumagamit ng global queue na may .userInitiated para sa isang gawain na ang resulta ay hinihintay ng user. Ang parehong mga diskarte ay nagtatapos sa pagbabalik sa DispatchQueue.main para i-update ang UI — ito ay isang mandatoryong kinakailangan sa iOS.

Serial vs Concurrent background queues

Ang GCD ay sumusuporta sa dalawang uri ng queue: serial (sunud-sunod) at concurrent (magkatulad). Ang mga serial queue ay nagsasagawa ng mga gawain nang isa-isa — ito ay maginhawa para sa pag-access sa isang shared resource (file, database) nang walang pag-lock. Ang mga concurrent queue ay nagsasagawa ng mga gawain nang magkatulad, na ipinamamahagi ang mga ito sa mga libreng core. Ang DispatchQueue.global — ay palaging concurrent. Upang lumikha ng isang serial queue, gamitin ang DispatchQueue(label: "com.app.queue").

Background Thread sa Android: Executors at Dispatchers

Android ay nagbibigay ng ilang antas ng abstraction para sa mga background thread. Ang klasikong diskarte — java.util.concurrent.Executors.newFixedThreadPool(n) o Executors.newCachedThreadPool(). Ang modernong diskarte — Kotlin Coroutines na may Dispatchers.IO (para sa input-output: network, mga file, database) at Dispatchers.Default (para sa CPU-intensive na mga gawain: pag-uuri, pagproseso ng imahe). WorkManager — para sa mga delayed at garantisadong background task.

HandlerThread — isang espesyal na klase ng Android para sa paglikha ng background thread na may sariling Looper (queue ng mensahe). Hindi tulad ng Executors, pinapayagan ng HandlerThread ang pagpapadala ng mga mensahe at Runnable sa pamamagitan ng Handler. Ginagamit para sa mga operasyon na nangangailangan ng pag-queue (hal., sunud-sunod na pagsulat sa database). Pagkatapos gamitin, dapat tawagan ang quit() o quitSafely() upang palayain ang mga mapagkukunan.

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

class DataRepository {

    private val ioExecutor = Executors.newFixedThreadPool(4)

    // Klasikong diskarte sa pamamagitan ng Executors
    fun loadDataLegacy(callback: (String) -> Unit) {
        ioExecutor.execute {
            val result = readFromFile()
            val handler = android.os.Handler(android.os.Looper.getMainLooper())
            handler.post { callback(result) }
        }
    }

    // Modernong diskarte sa pamamagitan ng Coroutines
    suspend fun loadDataCoroutines(): String {
        return withContext(Dispatchers.IO) {
            // Operasyon sa file — isinasagawa sa background pool
            readFromFile()
        }
        // Ang resulta ay awtomatikong bumabalik sa Dispatchers.Main
    }

    // CPU-intensive na gawain sa Dispatchers.Default
    suspend fun processImage(pixels: IntArray): IntArray {
        return withContext(Dispatchers.Default) {
            // Pag-uuri, pag-filter — isinasagawa sa Default pool
            pixels.sortedArray()
        }
    }

    private fun readFromFile(): String {
        Thread.sleep(1000) // simulasyon ng pagbabasa mula sa file
        return "file_content"
    }

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

Ang halimbawang DataRepository ay nagpapakita ng ebolusyon ng mga background thread sa Android. Ang legacy method na loadDataLegacy ay gumagamit ng Executors.newFixedThreadPool(4) na may Handler upang bumalik sa Main Thread. Ang modernong loadDataCoroutines ay gumagamit ng withContext(Dispatchers.IO) — ang coroutine ay nagpapa-pause habang nagtatrabaho, hindi hinaharangan ang thread, at awtomatikong nagpapatuloy sa Main Thread. Ang Dispatchers.Default ay inirerekomenda para sa mga CPU-bound na operasyon (pag-uuri, pag-filter, pagbabago ng data).

Coroutines bilang modernong pamantayan ng mga background task

Kotlin Coroutines — hindi lamang isang paraan ng pagtatrabaho sa mga thread, kundi isang pangunahing naiibang modelo: ang mga asynchronous na gawain ay hindi nakatali sa isang partikular na thread at maaaring i-suspend (suspend) nang hindi hinaharangan. Nangangahulugan ito na habang nasa background work, hindi sinasakop ng coroutine ang isang thread, ngunit pinalalaya ito para sa iba pang mga gawain. Ang mekanismo ng suspension ay nagpapahintulot sa pagpapatupad ng daan-daang libong concurrent na gawain sa isang pool ng 4-8 thread nang walang thread starvation.

Tatlong pangunahing dispatcher: Dispatchers.Main (UI, isang thread), Dispatchers.IO (64 na thread bilang default para sa mga blocking operation: network, mga file, database), Dispatchers.Default (katumbas ng bilang ng mga CPU core, para sa masinsinang pagkalkula). Sa pamamagitan ng pagsasama-sama ng mga ito sa pamamagitan ng withContext, ang developer ay lumilipat sa pagitan ng mga thread nang hindi gumagawa ng mga callback. Ang withContext ay isang suspend function na hindi nagbabalik ng kontrol hanggang sa makumpleto ang gawain.

kotlin
// Coroutines: komposisyon ng mga background task
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay

suspend fun loadUserProfile(userId: String): UserProfile =
    coroutineScope {
        // Parallel na pag-load ng data mula sa iba't ibang source
        val user = async(Dispatchers.IO) { fetchUser(userId) }
        val posts = async(Dispatchers.IO) { fetchPosts(userId) }
        val avatar = async(Dispatchers.Default) {
            processAvatar(fetchAvatar(userId))
        }

        // await() — nagpapa-pause hanggang makumpleto ang lahat ng gawain
        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 }

Ang function na loadUserProfile ay naglulunsad ng tatlong parallel background task sa pamamagitan ng async. Ang fetchUser at fetchPosts — IO-bound (network), ay isinasagawa sa Dispatchers.IO. Ang processAvatar — CPU-bound (pagproseso ng imahe), ay isinasagawa sa Dispatchers.Default. Ang await() ay nagpapa-pause ng coroutine hanggang makumpleto ang lahat ng gawain. Ang kabuuang oras ng pagpapatupad ay katumbas ng maximum na oras sa tatlong gawain (500 ms para sa fetchPosts), hindi ang kanilang kabuuan. Ito ang pangunahing bentahe ng coroutines kumpara sa sunud-sunod na pagpapatupad.

Structured concurrency: pagpigil sa mga pagtagas

Structured concurrency — prinsipyo kung saan ang bawat coroutine ay may parent scope, at ang pagkansela ng magulang ay awtomatikong kinakansela ang mga anak na coroutine. Sa Android, kinakansela ng lifecycleScope ang lahat ng coroutine kapag nawasak ang Activity. viewModelScope — kapag na-clear ang ViewModel. Pinipigilan nito ang pagtagas ng mga background task: kung isinara ng user ang screen, ang background coroutine ay hindi magpapatuloy sa pag-load ng data na hindi na kailangan ng sinuman.

WorkManager: mga background task para sa Android

WorkManager — library ng Android Jetpack para sa pagsasagawa ng mga background task na dapat isagawa kahit na matapos i-restart ang device o isara ang application. Hindi tulad ng Executors at coroutines, na nabubuhay sa proseso ng application, ang WorkManager ay nagpapasa ng gawain sa system dispatcher na ginagarantiyahan ang pagpapatupad sa ilalim ng angkop na mga kondisyon (availability ng network, antas ng baterya, libreng espasyo). Ang WorkManager ay angkop para sa pag-sync ng data, pag-upload ng mga log, pag-backup.

Ang isang gawain sa WorkManager ay isang klase na nagmamana ng Worker (o CoroutineWorker para sa coroutines). Ang Worker.doWork() ay isinasagawa sa isang background thread na ibinigay ng WorkManager. Ang resulta ay ibinabalik sa pamamagitan ng Result.success(), Result.retry() o Result.failure(). Ang mga gawain ay maaaring pagsamahin sa mga chain: oneTimeWorkRequest.andThen(nextRequest).enqueue(). Ang WorkManager mismo ang pumipili ng pinakamainam na oras ng pagpapatupad na isinasaalang-alang ang mga limitasyon (Constraints).

kotlin
// WorkManager na may coroutines
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 {
        // Isinasagawa sa Dispatchers.Default (default)
        return withContext(Dispatchers.IO) {
            try {
                syncDataToServer()
                Result.success()
            } catch (e: Exception) {
                if (runAttemptCount < 3) Result.retry() else Result.failure()
            }
        }
    }

    private suspend fun syncDataToServer() {
        // Simulasyon ng pag-sync
        delay(1000)
    }
}

// Paglulunsad ng WorkManager task na may mga limitasyon
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)
}

Ang SyncWorker ay nagmamana ng CoroutineWorker — bersyon ng Worker na sumusuporta sa coroutines. Ang doWork() ay isinasagawa sa Dispatchers.Default, paglipat sa IO para sa mga network operation sa pamamagitan ng withContext. Ginagarantiyahan ng Constraints na ang pag-sync ay magsisimula lamang kapag may network at ang antas ng baterya ay hindi mas mababa sa mababa. Ang BackoffCriteria na may EXPONENTIAL ay nagpapataas ng pagitan sa pagitan ng mga paulit-ulit na pagtatangka: 10, 20, 40 segundo.

PeriodicWorkRequest para sa regular na background task

Para sa regular na gawain (pag-sync bawat 15 minuto, pagpapadala ng analytics bawat oras) ang WorkManager ay nagbibigay ng PeriodicWorkRequestBuilder. Minimum na pagitan — 15 minuto. Hindi tulad ng OneTimeWorkRequest, hindi ginagarantiyahan ng PeriodicWorkRequest ang eksaktong pagsunod sa pagitan — maaaring pagsamahin ng system ang ilang periodic na gawain upang makatipid ng baterya. Para sa eksaktong pagitan, gamitin ang AlarmManager, ngunit isaalang-alang ang mga limitasyon ng Android 12+ para sa eksaktong mga alarm.

Mga karaniwang pagkakamali sa pagtatrabaho sa mga background thread

Unang pagkakamali — paggawa ng bagong Thread para sa bawat gawain. Ang new Thread().start() ay lumilikha ng isang native thread na may alokasyon na ~1 MB para sa stack. Para sa 100 parallel na gawain, ito ay 100 MB lamang para sa mga stack, kasama ang overhead para sa context switch. Gumamit ng mga pool ng thread: Executors.newFixedThreadPool(n) (Android) o DispatchQueue.global() (iOS) — sila ay muling gumagamit ng mga thread, binabawasan ang overhead ng sampu-sampung beses.

Pangalawang pagkakamali — pag-access sa mutable state mula sa maraming background thread nang walang synchronization. Kung ang dalawang background thread ay sabay na sumusulat sa parehong ArrayList o HashMap, nangyayari ang race condition: ConcurrentModificationException sa Android, pagkasira ng data sa iOS. Solusyon: gumamit ng thread-safe na mga koleksyon (ConcurrentHashMap, CopyOnWriteArrayList) o i-serialize ang access sa pamamagitan ng isang queue (DispatchQueue serial).

Pangatlong pagkakamali — mga background task na walang pamamahala ng lifecycle. Ang paglulunsad ng coroutine sa global scope nang walang pag-binding sa lifecycle ng Activity o ViewModel ay humahantong sa pagtagas: ang gawain ay patuloy na tumatakbo pagkatapos sirain ang screen. Sa Android, gamitin ang lifecycleScope (Activity/Fragment) o viewModelScope (ViewModel). Sa iOS — weak self sa mga closure at pagkansela ng mga gawain sa deinit.

Mga madalas itanong

Ano ang Background Thread sa mga mobile application?

Background Thread — thread kung saan isinasagawa ang mga operasyon na hindi nauugnay sa UI: mga kahilingan sa network, pagbabasa/pagsulat ng mga file, JSON parsing, mga pagkalkula. Pinapalaya nito ang Main Thread mula sa mabigat na trabaho, pinapanatili ang pagtugon ng interface. Sa iOS, ang mga background thread ay pinamamahalaan sa pamamagitan ng GCD (DispatchQueue.global), sa Android — sa pamamagitan ng Executors o Kotlin Coroutines (Dispatchers.IO, Dispatchers.Default).

Ano ang pagkakaiba ng Dispatchers.IO sa Dispatchers.Default?

Dispatchers.IO ay idinisenyo para sa mga blocking input-output operation: pagbabasa ng mga file, mga kahilingan sa network, pagtatrabaho sa database. Ito ay may pool na 64 na thread. Dispatchers.Default — para sa CPU-intensive na gawain: pag-uuri, pag-filter, pagproseso ng imahe. Ang pool nito ay katumbas ng bilang ng mga CPU core. Ang paggamit ng Dispatchers.Default para sa IO operation ay maaaring harangan ang lahat ng core, at ang Dispatchers.IO para sa CPU tasks — ay maaaring lumikha ng labis na bilang ng mga thread.

Paano lumipat sa isang background thread sa iOS?

DispatchQueue.global(qos: .background).async { } ay nagpapadala ng execution block sa global background queue. Pagkatapos makumpleto ang background work, kailangan mong bumalik sa pangunahing thread sa pamamagitan ng DispatchQueue.main.async { } upang i-update ang UI. Para sa sunud-sunod na background task, gamitin ang OperationQueue na may maxConcurrentOperationCount = 1 o DispatchQueue(label: "serial").

Ilang background thread ang maaaring gawin sa isang mobile application?

Ang inirerekomendang bilang ng background thread ay katumbas ng bilang ng mga CPU core plus 1 para sa IO-bound na gawain. Sa isang modernong 8-core device, ito ay 9 na thread. Ang paggawa ng daan-daang thread ay humahantong sa thread starvation: ang operating system ay gumugugol ng mas maraming oras sa paglipat ng konteksto (context switch) kaysa sa pagsasagawa ng mga gawain. Ang GCD sa iOS at Executors sa Android ay awtomatikong nag-o-optimize ng pool ng thread para sa kasalukuyang device.

Kailangan bang bumalik sa Main Thread pagkatapos ng coroutine?

Sa Kotlin Coroutines, ang pagbabalik sa Main Thread ay awtomatikong nangyayari kung ang coroutine ay inilunsad sa Main-scope (lifecycleScope.launch, viewModelScope.launch). Ang function na withContext(Dispatchers.IO) ay nagpapa-pause ng coroutine sa IO thread, at pagkatapos makumpleto ay awtomatikong ipagpapatuloy ito sa dispatcher kung saan ito inilunsad (karaniwan ay Main). Ang tahasang pagtawag sa DispatchQueue.main.async ay hindi kinakailangan.

Buod

  • Background Thread — thread para sa mga operasyon na hindi dapat isagawa sa Main Thread: network, mga file, JSON parsing, mga pagkalkula
  • iOS: DispatchQueue.global(qos:) at OperationQueue — pangunahing API para sa mga background task na may suporta sa QoS
  • Android: Executors, HandlerThread, WorkManager para sa Java; Dispatchers.IO/Default + coroutines para sa Kotlin
  • Coroutines na may withContext ay nagpapalit ng thread nang walang callback at nang walang pag-block ng thread (suspend mechanism)
  • WorkManager ay ginagarantiyahan ang pagpapatupad ng mga background task kahit na matapos i-restart ang device na isinasaalang-alang ang mga limitasyon
  • Mga pagkakamali: paggawa ng bagong Thread sa halip na pool, race condition sa pag-access sa mutable state, pagtagas dahil sa kawalan ng pag-binding sa lifecycle
  • Resulta mula sa background thread ay palaging ibinabalik sa Main Thread: sa pamamagitan ng Dispatchers.Main (Android) o DispatchQueue.main.async (iOS)

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din