ফ্রিজ (হ্যাং) হল এমন একটি অবস্থা যেখানে মোবাইল অ্যাপ্লিকেশন দীর্ঘ সময় ধরে ব্যবহারকারীর যেকোনো কর্মে সাড়া দেওয়া বন্ধ করে দেয়। ল্যাগ (ধীরগতি) এবং গ্লিচ (ভুল আচরণ) থেকে ভিন্ন, ফ্রিজ UI সম্পূর্ণরূপে ব্লক করে: টাচ প্রক্রিয়া হয় না, অ্যানিমেশন থেমে যায়, স্ক্রিন “জমে যায়”। কারণ হল সিঙ্ক্রোনাস অপারেশন দ্বারা মূল থ্রেড ব্লক হওয়া, মাল্টিথ্রেডেড কোডে ডেডলক, বা অস্বাভাবিকভাবে দীর্ঘ গার্বেজ কালেকশন। Apple Main Thread Checker ডকুমেন্টেশন অনুযায়ী, iOS-এর 40% এর বেশি ক্র্যাশ রিপোর্ট মূল থ্রেড ব্লকিংয়ের সাথে সম্পর্কিত। Android-এ, অনুরূপ পরিস্থিতি ANR — সিস্টেম ডায়ালগ “অ্যাপ সাড়া দিচ্ছে না” — এর দিকে নিয়ে যায়।
মূল বিষয়
ফ্রিজ (হ্যাং) মোবাইল অ্যাপ্লিকেশনে এমন একটি অবস্থা যেখানে অ্যাপ বেশ কয়েক সেকেন্ড বা তার বেশি সময় ধরে ইনপুট ইভেন্ট প্রক্রিয়া করা এবং ইন্টারফেস আপডেট করা বন্ধ করে দেয়। প্রযুক্তিগতভাবে, এর অর্থ হল মূল থ্রেড ব্লক হয়ে গেছে এবং পরবর্তী রান লুপ ইটারেশন সম্পাদন করতে পারে না।
ল্যাগ হল 500 ms পর্যন্ত বিলম্ব যেখানে ব্যবহারকারী ধীরগতি লক্ষ্য করেন কিন্তু অ্যাপ কাজ করতে থাকে। ফ্রিজ 1 সেকেন্ড থেকে দশক সেকেন্ড পর্যন্ত স্থায়ী হয়। Android-এ ANR হল ফ্রিজের একটি বিশেষ ক্ষেত্রে যা 5 সেকেন্ডের বেশি স্থায়ী হয়েছে এবং সিস্টেম দ্বারা শনাক্ত হয়েছে। প্রতিটি ফ্রিজ ANR-এর দিকে নিয়ে যায় না, কিন্তু প্রতিটি ANR হল একটি সিস্টেম-ডকুমেন্টেড ফ্রিজ।
Android-এ, 5 সেকেন্ডের বেশি স্থায়ী ফ্রিজ ANR ডায়ালগ ট্রিগার করে যা অ্যাপ বন্ধ করার প্রস্তাব দেয়। iOS-এ, সিস্টেমে একটি ওয়াচডগ আছে — যদি অ্যাপ 10–20 সেকেন্ড ধরে ইভেন্টে সাড়া না দেয়, ওয়াচডগ কোড 0x8badf00d (ate bad food) সহ প্রক্রিয়া শেষ করে দেয়। ব্যবহারকারী শুধু অ্যাপটি হঠাৎ হোম স্ক্রিনে বন্ধ হতে দেখেন।
যেকোনো অপারেশন যা 100 ms-এর বেশি সময় নেয় এবং মূল থ্রেডে চালু হয়, সম্ভাব্যভাবে ফ্রিজের কারণ হতে পারে। আসুন ব্লকেজের মূল উৎসগুলি দেখি।
বড় ফাইল পড়া, অ্যাসিঙ্ক্রোনি ছাড়া নেটওয়ার্ক রিকোয়েস্ট, সিঙ্ক্রোনাস apply পদ্ধতি এবং পরবর্তী commit দ্বারা SharedPreferences-এ ডেটা সংরক্ষণ — এই সমস্ত অপারেশন মূল থ্রেড ব্লক করে। Android-এ, 10 MB ফাইল সিঙ্ক্রোনাসভাবে পড়তে ফ্ল্যাশ মেমোরির গতির উপর নির্ভর করে 200–500 ms লাগতে পারে। iOS-এ, completionHandler ছাড়া সিঙ্ক্রোনাস URLSession লোড সার্ভার প্রতিক্রিয়া সময়ের জন্য UI ব্লক করে।
যখন দুটি থ্রেড একে অপরের দ্বারা ধারণকৃত সম্পদের জন্য অপেক্ষা করে, তখন ডেডলক হয়। মোবাইল অ্যাপ্লিকেশনে, একটি সাধারণ পরিস্থিতি হল থ্রেড A Lock1 লক করে এবং Lock2-এর জন্য অপেক্ষা করে, যখন থ্রেড B Lock2 লক করে এবং Lock1-এর জন্য অপেক্ষা করে। উভয় থ্রেড চিরতরে ফ্রিজ হয়ে যায়। যদি তাদের মধ্যে একটি মূল থ্রেড হয়, অ্যাপ সম্পূর্ণরূপে ফ্রিজ হয়ে যায়।
লজিক ত্রুটি — যেমন, প্রস্থান শর্ত ছাড়া while(true) বা বেস কেস ছাড়া রিকার্শন — মূল থ্রেডে অসীম সম্পাদনের দিকে নিয়ে যায়। Android এটি 5 সেকেন্ড পরে ANR-এর মাধ্যমে শনাক্ত করে, iOS — স্ট্যাকশটের মাধ্যমে, যা অসীমভাবে পুনরাবৃত্ত কল স্ট্যাক ক্যাপচার করে।
ফ্রিজ নির্ণয়ের জন্য এমন সরঞ্জাম প্রয়োজন যা ব্লকেজের মুহূর্তে সমস্ত থ্রেডের অবস্থা ক্যাপচার করতে সক্ষম।
প্রতিটি ANR-এ, Android সিস্টেম একটি ফাইল /data/anr/traces.txt সংরক্ষণ করে যাতে প্রতিটি অ্যাপ থ্রেডের স্ট্যাক ডাম্প থাকে। এই ফাইল বিশ্লেষণ প্রধান নির্ণয় পদ্ধতি: main থ্রেড খুঁজুন এবং দেখুন এটি কোন পদ্ধতিতে থেমেছে। যদি স্ট্যাক Thread.sleep, InputStream.read বা Lock.lock-এ শেষ হয় — কারণ পাওয়া গেছে।
Xcode অ্যাপ ফ্রিজ হলে (SIGSTOP সিগন্যাল) স্ট্যাকশট — সমস্ত থ্রেড স্ট্যাকের স্ন্যাপশট — নিতে পারে। স্কিমে “Logging” → “Include Stackshot Logs” সক্রিয় করুন। কোড 0x8badf00d সহ ক্র্যাশে, Devices & Simulators থেকে ক্র্যাশ লগ বের করুন এবং আটকে থাকা স্ট্যাক সহ com.apple.main-thread খুঁজুন।
Main Thread Checker অ্যাপ চলাকালে ব্যাকগ্রাউন্ড থ্রেড থেকে UIKit কল স্বয়ংক্রিয়ভাবে শনাক্ত করে। এটি স্কিমে সক্রিয় করুন (Diagnostics → Main Thread Checker)। প্রতিটি সতর্কতা ফ্রিজের সম্ভাব্য কারণ, বিশেষ করে যদি এটি নেটওয়ার্ক রিকোয়েস্ট completionHandler ক্লোজারে ঘটে।
Android-এ StrictMode-এর মাধ্যমে ব্লকেজ শনাক্তকরণের উদাহরণ:
class App : Application() {
override fun onCreate() {
super.onCreate()
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.build())
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.build())
}
}
ফ্রিজ দূর করা শুরু হয় সমস্ত সম্ভাব্য দীর্ঘ অপারেশন ব্যাকগ্রাউন্ড থ্রেডে স্থানান্তরের মাধ্যমে। আসুন প্রতিটি প্ল্যাটফর্মের জন্য নির্দিষ্ট কৌশল দেখি।
Kotlin Coroutines viewModelScope.launch(Dispatchers.IO) সহ গ্যারান্টি দেয় যে নেটওয়ার্ক অপারেশন বা ডেটাবেস রিড ব্যাকগ্রাউন্ড থ্রেডে সম্পাদিত হবে। Dispatchers.Main শুধুমাত্র UI আপডেটের জন্য ব্যবহৃত হয়। গুরুত্বপূর্ণ: সমস্ত suspend ফাংশন স্ট্রাকচার্ড হতে হবে — চাইল্ড কোরুটিন প্যারেন্ট ক্যান্সেল হলে ক্যান্সেল হয়, থ্রেড লিক প্রতিরোধ করে।
Grand Central Dispatch ব্যাকগ্রাউন্ড কাজের জন্য DispatchQueue.global(qos: .userInitiated) এবং UI আপডেটের জন্য DispatchQueue.main.async সহ স্ট্যান্ডার্ড প্যাটার্ন। মূল কিউতে sync() এড়িয়ে চলুন — এটি গ্যারান্টেড ডেডলক। MainActor-এর মাধ্যমে মূল থ্রেডে স্বয়ংক্রিয় প্রত্যাবর্তন সহ আরও পাঠযোগ্য অ্যাসিঙ্ক্রোনাস কোডের জন্য async/await (Swift 5.5+) ব্যবহার করুন।
মূল থ্রেডে Kotlin-এ synchronized ব্লক এবং Swift-এ @synchronized বিপজ্জনক: যদি অন্য কোনো থ্রেড ইতিমধ্যে এই লক অর্জন করে থাকে, মূল থ্রেড অপেক্ষায় ফ্রিজ হবে। লকের পরিবর্তে পরমাণু প্রকার (AtomicInteger, Swift-এ পরমাণু প্রপার্টি) বা সিরিয়াল কিউ ব্যবহার করুন।
Android-এ কোরুটিন সহ অ্যাসিঙ্ক্রোনাস ডেটা লোডিংয়ের উদাহরণ:
class DataViewModel : ViewModel() {
private val _data = MutableStateFlow<List<Item>>(emptyList())
val data: StateFlow<List<Item>> = _data.asStateFlow()
fun loadData() {
viewModelScope.launch(Dispatchers.IO) {
val result = fetchFromNetwork()
_data.emit(result)
}
}
}
সরঞ্জাম, আর্কিটেকচার নীতি এবং কোড রিভিউ প্রক্রিয়ার সমন্বয় পদ্ধতিগতভাবে ফ্রিজ প্রতিরোধে সহায়তা করে।
থ্রেড নীতির জন্য StrictMode penaltyDeath সহ কনফিগার করুন — এটি মূল থ্রেডে নেটওয়ার্ক কল বা ডিস্ক I/O শনাক্ত হলে তাৎক্ষণিক অ্যাপ ক্র্যাশ ঘটাবে। ডেভেলপার সমস্যা উপেক্ষা করতে পারবেন না। প্রোডাকশন বিল্ডে, ক্র্যাশ ছাড়া পরিসংখ্যান সংগ্রহ করতে penaltyLog ব্যবহার করুন।
iOS-এ, Debug স্কিমে Main Thread Checker সক্রিয় করুন এবং CI-কে এই অপশন সহ পরীক্ষা চালাতে কনফিগার করুন। যদি কোনো পরীক্ষায় ব্যাকগ্রাউন্ড থ্রেড থেকে UIKit কল থাকে — তবে এটি ব্যর্থ হওয়া উচিত। TestFlight-এ পাঠানোর আগে সমস্যা শনাক্ত করার এটিই একমাত্র নির্ভরযোগ্য উপায়।
কোড রিভিউ প্রক্রিয়ায় একটি বাধ্যতামূলক পয়েন্ট যোগ করুন: যাচাই করুন যে কোনো নেটওয়ার্ক কল, ফাইল অপারেশন, ডেটাবেস অ্যাক্সেস বা ভারী গণনা ব্যাকগ্রাউন্ড থ্রেডে চলে। ডেডলক স্ট্যাটিক বিশ্লেষক দিয়ে শনাক্ত করা যায়: Facebook-এর Infer এবং Xcode-এর Thread Safety Checker রানটাইমের আগে সম্ভাব্য লক খুঁজে পায়।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
ANR (Application Not Responding) একটি Android সিস্টেম বিজ্ঞপ্তি যা মূল থ্রেড 5 সেকেন্ডের বেশি ফ্রিজ হলে দেখা যায়। ফ্রিজ একটি বিস্তৃত ধারণা: যেকোনো সময়ের যেকোনো UI ব্লকেজ। iOS-এ ANR নেই, তবে 10–20 সেকেন্ড টাইমআউট সহ একটি Watchdog আছে।
ফাইলটি /data/anr/traces.txt-এ অবস্থিত। অ্যাক্সেসের জন্য রুট অ্যাক্সেস বা adb shell প্রয়োজন: রুট অধিকার সহ adb shell cat /data/anr/traces.txt \> traces.txt চালান। স্ট্যাকে “main” থ্রেড খুঁজুন — শেষ কল করা পদ্ধতি ব্লকেজের কারণ নির্দেশ করে।
যদি ফ্রিজ 10 সেকেন্ড-এর কম স্থায়ী হয়, Watchdog ট্রিগার হয় না, এবং অ্যাপ ব্লকিং অপারেশন সম্পূর্ণ না হওয়া পর্যন্ত simply “আটকে” থাকে। ব্যবহারকারী ক্র্যাশ দেখেন না কিন্তু হতাশ হন। এই ধরনের ক্ষেত্রে শনাক্ত করতে, কাস্টম নির্বাহ সময় ট্রেস সহ MetricKit ব্যবহার করুন।
UI পরীক্ষা ব্যবহার করুন যাচাই সহ যে স্ক্রিন < 1 সেকেন্ডে খোলে। ট্যাপ এবং পরবর্তী স্ক্রিন উপস্থিতির মধ্যে সময় মাপ CI-তে যোগ করুন। Android-এ, অ্যাসিঙ্ক্রোনাস অপারেশনের অপেক্ষায় IdlingResource সহ Espresso ব্যবহার করুন। iOS-এ, লোডিং সময় যাচাই করতে XCTWaiter সহ XCTest ব্যবহার করুন।
SwiftUI নিজে ফ্রিজের কারণ হয় না, কিন্তু body প্রপার্টিতে জটিল গণনা করে। যদি ভারী অপারেশনের কারণে body গণনা করতে 500 ms লাগে, UI ফ্রিজ হয়। সমাধান হল গণনা Task.detached-এ স্থানান্তর এবং প্রধান অ্যাক্টরে @State অ্যাসিঙ্ক্রোনাসভাবে আপডেট করা।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন