Main Thread — موبائل ایپلیکیشنز میں عملدرآمد کا مرکزی تھریڈ ہے جو پورے یوزر انٹرفیس کو سنبھالتا ہے: ٹچز، رینڈرنگ، لے آؤٹ اپ ڈیٹس اور اینی میشنز۔ iOS میں یہ RunLoop.main ہے، Android میں — Looper.getMainLooper()۔ اس تھریڈ پر کوئی بھی طویل آپریشن UI کو بلاک کرتا ہے اور ANR (Android) یا انٹرفیس منجمد (iOS) کا سبب بنتا ہے۔ Apple UIKit دستاویزات کے مطابق، UI کلاسز تھریڈ سیف نہیں ہیں اور صرف Main Thread سے کال کی ضرورت ہوتی ہے۔
اہم نکات
Main Thread وہ تھریڈ ہے جو ایپلیکیشن شروع ہونے پر آپریٹنگ سسٹم کے ذریعے بنایا جاتا ہے اور تمام یوزر انٹرفیس ایونٹس کو سنبھالنے کا ذمہ دار ہوتا ہے۔ موبائل پلیٹ فارمز کے سیاق و سباق میں، Main Thread کو UI Thread بھی کہا جاتا ہے، کیونکہ رینڈرنگ، ٹچ ہینڈلنگ اور اینی میشنز سے متعلق تمام آپریشنز اسی پر عملدرآمد ہوتے ہیں۔ ہر ایپلیکیشن میں بالکل ایک Main Thread ہوتا ہے، اور تمام UI فریم ورک (UIKit, AppKit, Android Views, Compose UI) تھریڈ غیر محفوظ ہیں — وہ دوسرے تھریڈز سے کال کرنے پر درست کام کرنے کی ضمانت نہیں دیتے۔
فن تعمیر کے لحاظ سے، Main Thread ایونٹ لوپ پیٹرن کو نافذ کرتا ہے: تھریڈ نئے ایونٹس (ٹچز، سسٹم نوٹیفیکیشنز، ٹائمرز) کا لامحدود انتظار کرتا ہے اور انہیں قطار کی ترتیب میں پروسیس کرتا ہے۔ جب ایک ایونٹ پروسیس ہو رہا ہوتا ہے، اگلا قطار میں انتظار کرتا ہے۔ اگر پروسیسنگ میں 100-200 ملی سیکنڈ سے زیادہ وقت لگتا ہے، تو صارف تاخیر (jank) محسوس کرتا ہے۔ اگر 5 سیکنڈ (Android) سے زیادہ — سسٹم ANR (Application Not Responding) ڈائیلاگ دکھاتا ہے اور ایپلیکیشن بند کرنے کی پیشکش کرتا ہے۔
Main Thread کو سمجھنے کی اہمیت کو بڑھا چڑھا کر نہیں بتایا جا سکتا: یہ موبائل ایپلیکیشنز میں 90% کارکردگی کے مسائل کا ذریعہ ہے۔ ڈویلپرز اکثر بھاری آپریشنز (نیٹ ورک، فائلیں، JSON پارسنگ، امیج کمپریشن) کو بیک گراؤنڈ تھریڈز پر منتقل کرنا بھول جاتے ہیں۔ ایک آپریشن جو ایمولیٹر پر 10 ملی سیکنڈ لیتا ہے، سست ڈسک والے حقیقی ڈیوائس پر 500 ملی سیکنڈ لے سکتا ہے اور نمایاں تاخیر کا سبب بن سکتا ہے۔
تھریڈ غیر محفوظ UI فریم ورک UIKit (2007) اور Android (2008) کے پہلے ورژن میں لیا گیا ایک آرکیٹیکچرل فیصلہ ہے۔ بنیادی وجہ کارکردگی ہے: لاک کے ذریعے UI اجزاء تک رسائی کو ہم آہنگ کرنا ہر رینڈرنگ آپریشن پر اوور ہیڈ ڈالے گا۔ اس کے بجائے، فریم ورک تمام UI تبدیلیوں کو سختی سے ایک ہی تھریڈ پر انجام دینے کی ضرورت کرتے ہیں، بغیر اوور ہیڈ کے ریس کنڈیشن کو ختم کرتے ہوئے۔
تصور کریں کہ دو بیک گراؤنڈ تھریڈ ایک ساتھ textView.setText() کال کر رہے ہیں۔ اگر UI تھریڈ سیف ہوتا، تو دونوں کالز mutex کے ذریعے ہم آہنگ ہوتیں، رینڈرنگ کو 20-40% سست کر دیتیں۔ موجودہ فن تعمیر میں، بیک گراؤنڈ تھریڈ سے کوئی بھی UI کال یا تو نظر انداز کر دی جاتی ہے یا کریش کا سبب بنتی ہے (iOS میں — Main Thread Checker Exception, Android میں — CalledFromWrongThreadException)۔ مستثنیٰ Android میں SurfaceView اور TextureView ہیں، جہاں رینڈرنگ علیحدہ تھریڈ سے کی جا سکتی ہے۔
جدید موبائل فریم ورک (SwiftUI, Jetpack Compose) اس حد کو برقرار رکھتے ہیں: SwiftUI کو تمام State اور ObservedObject تبدیلیوں کے لیے Main Thread پر ہونا ضروری ہے، اگرچہ رینڈرنگ خود جزوی طور پر بیک گراؤنڈ تھریڈز پر منتقل کر دیا جاتا ہے۔ Jetpack Compose بھی Main Thread پر State میں تبدیلی کی توقع کرتا ہے۔ مستثنیٰ drawBehind اور layout سے متعلق Compose موڈیفائر ہیں، جنہیں واضح طور پر دستاویز کرنے پر دوسرے تھریڈز سے کال کیا جا سکتا ہے۔
DispatchQueue.main — iOS میں Main Thread پر کوڈ بھیجنے کا بنیادی طریقہ کار ہے۔ یہ ایپلیکیشن کے مرکزی RunLoop سے منسلک ایک سیریل قطار ہے۔ اسے بھیجے گئے تمام بلاکس ترتیب وار، آمد کی ترتیب میں عملدرآمد ہوتے ہیں۔ اگر آپ Main Thread سے State میں تبدیلی کرتے ہیں یا setNeedsLayout() کال کرتے ہیں تو SwiftUI اور UIKit خود بخود اپ ڈیٹ ہو جاتے ہیں۔ بیک گراؤنڈ کام سے نتائج کی غیر مطابقت پذیر واپسی کے لیے، DispatchQueue.main.async {} استعمال کریں۔
Objective-C-Swift برج میں، Thread.isMainThread بھی دستیاب ہے — ایک خاصیت جو چیک کرتی ہے کہ موجودہ کوڈ مرکزی تھریڈ پر عملدرآمد کر رہا ہے یا نہیں۔ موجودہ UIKit پروجیکٹس کے لیے، یہ ایک معیاری پیٹرن ہے: if Thread.isMainThread { updateUI() } else { DispatchQueue.main.async { updateUI() } }۔ SwiftUI میں یہ چیک عام طور پر ضروری نہیں ہے، کیونکہ فریم ورک body اور modifier کے Main Thread پر عملدرآمد کی ضمانت دیتا ہے۔
import UIKit
class ViewController: UIViewController {
let imageView = UIImageView()
func loadImageFromNetwork() {
// بیک گراؤنڈ تھریڈ: تصویر ڈاؤن لوڈ ہو رہی ہے
DispatchQueue.global(qos: .background).async { [weak self] in
guard let url = URL(string: "https://example.com/image.png"),
let data = try? Data(contentsOf: url),
let image = UIImage(data: data)
else { return }
// UI اپ ڈیٹ کرنے کے لیے Main Thread پر واپس جائیں
DispatchQueue.main.async {
self?.imageView.image = image
self?.imageView.setNeedsLayout()
}
}
}
// چیک کریں کہ کوڈ Main Thread پر عملدرآمد کر رہا ہے
func safeUpdateUI() {
if Thread.isMainThread {
updateUI()
} else {
DispatchQueue.main.async {
self.updateUI()
}
}
}
private func updateUI() {
print("Main Thread پر UI اپ ڈیٹ کیا گیا")
}
}
مثال میں، loadImageFromNetwork() صحیح پیٹرن کو ظاہر کرتا ہے: URLSession یا Data(contentsOf:) DispatchQueue.global کے ذریعے بیک گراؤنڈ تھریڈ پر عملدرآمد ہوتا ہے، جس کے بعد نتیجہ UIImageView کو اپ ڈیٹ کرنے کے لیے DispatchQueue.main پر واپس آتا ہے۔ DispatchQueue.main.async کے بغیر، بیک گراؤنڈ تھریڈ سے UIKit کال کرنے پر ایپلیکیشن NSInternalInconsistencyException کے ساتھ کریش ہو جائے گی۔
iOS میں Main Thread پر کوڈ عملدرآمد کرنے کا سب سے قابل اعتماد طریقہ DispatchQueue.main.async کے ذریعے واضح بھیجنا ہے۔ یہاں تک کہ اگر آپ پہلے سے Main Thread پر ہیں، async بھیجنا مسائل پیدا نہیں کرتا: GCD اسے اگلے RunLoop تکرار پر پروسیس کرتا ہے۔ مطابقت پذیر عملدرآمد کے لیے، DispatchQueue.main.sync استعمال کریں، لیکن اگر Main Thread سے کال کیا جائے تو یہ ڈیڈ لاک کا سبب بن سکتا ہے۔ قاعدہ: نتائج واپس کرنے کے لیے async، sync صرف اس وقت جب آپ کو یقین ہو کہ آپ مرکزی تھریڈ پر نہیں ہیں۔
RunLoop.main ایک CFRunLoop آبجیکٹ ہے جو iOS کے مرکزی ایونٹ قطار سے منسلک ہے۔ یہ ان پٹ ذرائع (ٹچ ایونٹس)، ٹائمرز اور DispatchQueue.main بلاکس کو پروسیس کرتا ہے۔ ہر رینڈرنگ فریم (60/120 FPS) کے لیے ضروری ہے کہ عمودی سنک پلس (VSync) سے پہلے RunLoop میں تمام آپریشنز مکمل ہوں۔ اگر Main Thread پر آپریشنز 16.6 ms (60 FPS) یا 8.3 ms (120 FPS) سے زیادہ وقت لیتی ہیں، تو ایپلیکیشن فریم گرادیتی ہے، جو بصری طور پر jank یا stutter کے طور پر ظاہر ہوتا ہے۔
Looper.getMainLooper() — مرکزی تھریڈ کے ساتھ کام کرنے کے لیے Android کا بنیادی طریقہ کار ہے۔ Android میں ہر Main Thread میں ایک Looper ہوتا ہے جو قطار (MessageQueue) سے پیغامات کو لامحدود طور پر نکالتا ہے اور انہیں پروسیسنگ کے لیے Handler کو بھیجتا ہے۔ Activity.runOnUiThread() اور View.post() Handler(Looper.getMainLooper()) کے ارد گرد اعلیٰ سطحی ریپر ہیں۔ Dispatchers.Main کے ساتھ Kotlin Coroutines مرکزی تھریڈ پر واپس آنے کا جدید طریقہ ہے۔
Android StrictMode بھی فراہم کرتا ہے — Main Thread کو بلاک کرنے والے آپریشنز کا پتہ لگانے کا ایک ٹول۔ StrictMode.setThreadPolicy() آپ کو ایک پالیسی سیٹ کرنے کی اجازت دیتا ہے: مرکزی تھریڈ پر نیٹ ورک کالز (NetworkPolicy)، ڈسک ریڈ (DiskRead)، ڈسک رائٹ (DiskWrite) کی ممانعت۔ جب پالیسی کی خلاف ورزی ہوتی ہے، تو ایک استثناء پیدا ہوتا ہے یا logcat میں ایک پیغام لکھا جاتا ہے۔
// Android: Main Thread اور Kotlin Coroutines کے ساتھ کام کرنا
import android.os.Bundle
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URL
class MainActivity : ComponentActivity() {
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
textView = TextView(this)
setContentView(textView)
// مثال: غیر مطابقت پذیر ڈیٹا لوڈنگ
lifecycleScope.launch {
val result = loadData() // Dispatchers.IO پر عملدرآمد ہو رہا ہے
textView.text = result // Main Thread پر UI
}
}
private suspend fun loadData(): String {
return withContext(Dispatchers.IO) {
URL("https://api.example.com/data").readText()
}
}
}
// Main Thread کی خلاف ورزیوں کا پتہ لگانے کے لیے StrictMode
class App : Application() {
override fun onCreate() {
super.onCreate()
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)
}
}
Kotlin مثال lifecycleScope.launch کے ذریعے Dispatchers.Main اور withContext کے ذریعے Dispatchers.IO کے صحیح استعمال کو ظاہر کرتی ہے۔ تمام نیٹ ورک کا کام IO ڈسپیچر پر کیا جاتا ہے، جبکہ TextView اپ ڈیٹ خود بخود Main Thread پر ہوتا ہے، کیونکہ lifecycleScope میں launch ڈیفالٹ طور پر Dispatchers.Main استعمال کرتا ہے۔ Application.onCreate() میں StrictMode مرکزی تھریڈ پر اتفاقی نیٹ ورک کالز اور ڈسک آپریشنز کو روکتا ہے۔
Main Thread Checker — Xcode میں بلٹ ان ٹول (Xcode 9 سے دستیاب) ہے جو بیک گراؤنڈ تھریڈز سے UIKit, AppKit اور دیگر UI فریم ورک میں کالز کا پتہ لگاتا ہے۔ ڈیبگنگ کے دوران، Main Thread Checker تمام UI-API کالز کا تجزیہ کرتا ہے اور خلاف ورزی کا پتہ لگانے پر تفصیلی اسٹیک ٹریس کے ساتھ بریک پوائنٹ دکھاتا ہے۔ حقیقی ڈیوائسز پر (ریلیز بلڈ میں)، Main Thread Checker کام نہیں کرتا — خلاف ورزیاں کریش یا غلط رویے کے طور پر ظاہر ہوتی ہیں۔
Android میں، مساوی StrictMode (اوپر بیان کیا گیا) اور بلٹ ان لاگ ڈیٹیکٹر ہے: بیک گراؤنڈ تھریڈ سے View.setText() یا View.invalidate() کال کرنے پر، Android CalledFromWrongThreadException پھینکتا ہے۔ اضافی طور پر، Android Studio Profiler دکھاتا ہے کہ Main Thread پر کون سے آپریشنز عملدرآمد کر رہے ہیں۔ اگر آپ Main Thread پر نیٹ ورک یا فائل آپریشنز دیکھتے ہیں — یہ مسئلے کی واضح علامت ہے۔
| ٹول | پلیٹ فارم | یہ کیا پتہ لگاتا ہے |
|---|---|---|
| Main Thread Checker | iOS (Xcode) | بیک گراؤنڈ تھریڈز سے UIKit/AppKit کالز |
| StrictMode | Android | Main Thread پر نیٹ ورک، ڈسک، طویل آپریشنز |
| Android Studio Profiler | Android | وقت کے ساتھ Main Thread لوڈ کا تصور |
| Time Profiler | iOS (Instruments) | Main Thread پر میتھڈ عملدرآمد کے وقت کی پیمائش |
| HUD / DispatchQueue.main.async | iOS | ڈیبگنگ کے ذریعے UI بلاک کرنے کا بصری اشارہ |
Main Thread بلاک کرنے کی سب سے نمایاں علامت جھٹکے دار اسکرول (janky scroll) ہے۔ جب صارف UITableView یا RecyclerView کو اسکرول کرتا ہے، سسٹم توقع کرتا ہے کہ اگلا فریم 16 ms میں تیار ہو جائے گا۔ اگر Main Thread پر امیج ڈی کوڈنگ یا JSON پارسنگ کی جا رہی ہے، تو فریم رینڈرنگ میں تاخیر ہوتی ہے اور صارف جھٹکے دیکھتا ہے۔ تشخیص کے لیے، پروفائلر استعمال کریں: اگر prepareDisplay() یا layoutSubviews() >16 ms لیتا ہے — ڈیٹا غلط تھریڈ پر پروسیس ہو رہا ہے۔
پہلا منظر — Main Thread پر URLConnection یا Data(contentsOf:) کے ذریعے مطابقت پذیر نیٹ ورک کی درخواست۔ Android میں، detectNetwork() کے ساتھ StrictMode فوری طور پر اس خلاف ورزی کو پکڑ لیتا ہے۔ iOS میں، ایک مطابقت پذیر URLSession واضح غلطی نہیں دیتا، لیکن درخواست کے دوران (1-10 سیکنڈ) UI منجمد ہو جاتا ہے۔ حل: غیر مطابقت پذیر کال بیک کے ساتھ URLSession.dataTask (iOS) یا Retrofit/OkHttp (Android) استعمال کریں۔
دوسرا منظر — امیج ڈی کوڈنگ اور کمپریشن۔ Android میں مرکزی تھریڈ پر UIImage(data:) یا BitmapFactory.decodeResource() jank کی سب سے عام وجوہات میں سے ایک ہے۔ 4000x3000 پکسلز کی ایک تصویر 50-150 ملی سیکنڈ میں ڈی کوڈ ہوتی ہے، جو 16 ms کی حد سے تجاوز کرتی ہے۔ حل: ImageLoader (Kingfisher, Coil, Glide) استعمال کریں، جو بیک گراؤنڈ تھریڈ پر ڈی کوڈنگ کی ضمانت دیتے ہیں۔
تیسرا منظر — JSON پارسنگ۔ Main Thread پر JSONSerialization (iOS) یا JSONObject (Android) کے ذریعے API جواب کو پارس کرنا۔ یہاں تک کہ ایک چھوٹا 100 KB JSON 5-15 ملی سیکنڈ میں پارس ہوتا ہے، لیکن سست ڈیوائسز پر — 50 ملی سیکنڈ تک۔ دوسرے آپریشنز کے ساتھ مل کر، یہ جمع ہوتا ہے اور فریم ڈراپ کا نتیجہ دیتا ہے۔ حل: kotlinx.serialization/Decodable استعمال کریں جس میں parse() بیک گراؤنڈ تھریڈ پر کال کیا جائے، صرف نتیجہ اسائنمنٹ Main Thread پر چھوڑیں۔
اکثر پوچھے گئے سوالات
Main Thread ایپلیکیشن کا مرکزی تھریڈ ہے جس پر تمام UI آپریشنز انجام دیے جاتے ہیں: ٹچ ہینڈلنگ، اسکرین رینڈرنگ، اینی میشنز، لے آؤٹ اپ ڈیٹس۔ iOS میں یہ RunLoop.main اور DispatchQueue.main ہے، Android میں — Looper.getMainLooper()۔ تمام UI فریم ورک (UIKit, Android Views) تھریڈ غیر محفوظ ہیں اور صرف Main Thread سے کال کی ضرورت ہوتی ہے۔ اس تھریڈ پر کوئی بھی طویل آپریشن انٹرفیس کو بلاک کرتا ہے۔
UI فریم ورک کارکردگی کے لیے آرکیٹیکچرل طور پر تھریڈ غیر محفوظ ہیں: لاک کے ذریعے رسائی کو ہم آہنگ کرنا ہر رینڈرنگ آپریشن پر 20-40% اوور ہیڈ ڈالے گا۔ UIKit اور Android ڈویلپرز نے سنگل تھریڈ ماڈل کا انتخاب کیا جہاں mutex کے بغیر ریس کنڈیشن ختم ہو جاتی ہے۔ تمام UI تبدیلیاں سختی سے Main Thread پر انجام دی جانی چاہئیں — ورنہ کریش یا غلط ڈسپلے۔
iOS میں مرکزی قطار پر کوڈ بھیجنے کے لیے DispatchQueue.main.async { } استعمال کریں۔ Android میں — runOnUiThread { } یا Dispatchers.Main کے ساتھ Kotlin Coroutines۔ جدید طریقہ coroutines ہے: بیک گراؤنڈ کام کے لیے withContext(Dispatchers.IO) اور launch میں خودکار Dispatchers.Main۔ Java پروجیکٹس کے لیے، Handler(Looper.getMainLooper()).post { }۔
ANR (Application Not Responding) ایک Android ڈائیلاگ ہے جو ظاہر ہوتا ہے اگر Main Thread 5 سیکنڈ سے زیادہ بلاک رہے۔ ANR کا مطلب ہے کہ سسٹم کو ایپلیکیشن سے ان پٹ ایونٹ (ٹچ، کی پریس) پر جواب نہیں ملا یا BroadcastReceiver 10 سیکنڈ میں مکمل نہیں ہوا۔ وجہ Main Thread پر ایک مطابقت پذیر آپریشن ہے: نیٹ ورک کی درخواست، ڈیٹابیس کا کام، پیچیدہ حسابات۔ iOS میں، مساوی ڈائیلاگ کے بغیر UI منجمد ہونا ہے۔
SwiftUI خود بخود ضمانت دیتا ہے کہ body اور modifier Main Thread پر عملدرآمد کریں گے۔ تاہم، بیک گراؤنڈ تھریڈ سے @Published خصوصیات یا State میں تبدیلیاں (مثال کے طور پر، URLSession ڈیلیگیٹ سے) مسائل پیدا کر سکتی ہیں۔ ObservableObject کلاسز کے لیے @MainActor استعمال کریں تاکہ ان کے تمام طریقے Main Thread پر عملدرآمد کریں۔ SwiftUI 5.5+ میں، @MainActor ObservableObject کے لیے خود بخود شامل کیا جاتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں