LeakCanary Square کی ایک اوپن سورس لائبریری ہے جو Android ایپلیکیشنز میں خودکار میموری لیک کا پتہ لگانے کے لیے ہے۔ یہ ڈیولپمنٹ کے عمل میں ضم ہو جاتی ہے اور Activity، Fragment، ViewModel اور دیگر اجزاء کے لائف سائیکل کو ریئل ٹائم میں مانیٹر کرتی ہے، لیک ہوتے ہی اشارہ دیتی ہے۔ Square Open Source کے مطابق، یہ لائبریری ہزاروں پروجیکٹس میں استعمال ہوتی ہے اور Android پر میموری تشخیص کے لیے ڈی فیکٹو معیار سمجھی جاتی ہے۔
اہم نکات
LeakCanary Square کے ذریعے تیار کردہ Android ایپلیکیشنز میں خودکار میموری لیک کا پتہ لگانے کے لیے ایک لائبریری ہے۔ یہ ایپ بلڈ کے عمل میں ضم ہو جاتی ہے اور خودکار طور پر نگرانی کرتی ہے کہ جن اشیاء کو تباہ ہو جانا چاہیے (Activity، Fragment، View) وہ میموری میں رہتی ہیں یا نہیں۔ لیک کا پتہ چلنے پر، LeakCanary ہیپ ڈمپ بناتا ہے اور شے کو تھامے رکھنے والے حوالہ جات کے سلسلے کا تجزیہ کرتا ہے۔
یہ لائبریری Android کمیونٹی میں ایک معیار بن گئی ہے: GitHub کے مطابق، پروجیکٹ کو 28 ہزار سے زیادہ ستارے مل چکے ہیں اور یہ Google، Uber، Airbnb اور Facebook کی ایپلیکیشنز میں استعمال ہوتی ہے۔ LeakCanary دو اہم ورژنز میں دستیاب ہے: کلاسک 1.x (دستی ترتیب کے ساتھ) اور جدید 2.x (ContentProvider کے ذریعے خودکار انضمام)۔ ورژن 2.x میں Application کلاس میں ترمیم کی ضرورت نہیں — مکمل فعالیت کے لیے انحصار کافی ہے۔
LeakCanary کا بنیادی کام اس صورت حال کا پتہ لگانا ہے جب کوئی شے اپنے لائف سائیکل کے ختم ہونے کے بعد بھی میموری میں موجود رہتی ہے۔ یہ جامد فیلڈز، سنگلٹن، غیر رجسٹرڈ کال بیکس، گمنام کلاسز اور بیرونی اشیاء کو کیپچر کرنے والے کلوزرز کے ذریعے لیک کے لیے عام ہے۔
موبائل آلات پر محدود RAM کی وجہ سے Android پر میموری لیک ڈیسک ٹاپ کے مقابلے میں زیادہ سنگین ہیں۔ ہر اسکرین ٹرانزیشن پر 5–10 MB کا لیک بھی 30–40 منٹ کے ایپ استعمال کے بعد OutOfMemoryError کا سبب بن سکتا ہے۔ LeakCanary ایسے مسائل کو ڈیولپمنٹ مرحلے میں ہی دریافت کر لیتا ہے، پروڈکشن میں کریش کا انتظار کیے بغیر۔
LeakCanary کمزور حوالہ جات (WeakReference) کو جبری کوڑا کرکٹ جمع کرنے کے ساتھ ملا کر استعمال کرتا ہے۔ جب کوئی Activity یا Fragment onDestroy کال کرتا ہے، LeakCanary اس شے پر WeakReference بناتا ہے اور تھوڑی تاخیر (بطور ڈیفالٹ 5 سیکنڈ) کے بعد GC کو ٹرگر کرتا ہے۔ اگر GC کے بعد شے WeakReference کے ذریعے اب بھی قابل رسائی ہے، تو یہ ایک مضبوط حوالہ کے ذریعے تھامی ہوئی ہے — ایک لیک ریکارڈ کی جاتی ہے۔
لیک کا پتہ لگنے کے بعد، LeakCanary ایک ہیپ ڈمپ (میموری ڈمپ) بناتا ہے — HPROF فارمیٹ میں ایپلیکیشن میموری کا مکمل سنیپ شاٹ۔ اس کے بعد بلٹ ان تجزیہ کار (ورژن 2.x کے لیے Shark) GC Roots سے لیک شدہ شے تک رسائی کا گراف بناتا ہے اور مختصر ترین راستہ ڈھونڈتا ہے — وہ حوالہ جات کا سلسلہ جو شے کو میموری میں رکھے ہوئے ہے۔
// LeakCanary کا پتہ لگانے کی آسان منطق
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // جبری GC
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // شے GC سے بچ گئی — یہ لیک ہے
}
}
}
}
اہم نکتہ GcTrigger.runGc() کا جبری کال ہے۔ اس کے بغیر، حقیقی طور پر لیک ہونے والی شے اور اس شے کے درمیان فرق کرنا ناممکن ہے جسے GC نے ابھی جمع نہیں کیا۔ LeakCanary ایسا تین بار کرتا ہے: اگر تین GC چکروں کے بعد بھی شے میموری میں ہے، تو لیک کی تصدیق ہو جاتی ہے۔
Shark LeakCanary 2.x میں شامل ہیپ ڈمپ تجزیہ کار ہے، جو Kotlin میں لکھا گیا ہے۔ پچھلے HAHA تجزیہ کار کے برعکس، Shark پوری HPROF فائل کو میموری میں لوڈ نہیں کرتا، بلکہ کم سے کم مختص کے ساتھ اس کے آبجیکٹ گراف کو عبور کرتا ہے۔ یہ تجزیہ کے دوران RAM کی کھپت کو 50 MB سے کم کر کے 2–5 MB کر دیتا ہے اور تجزیہ کے وقت کو 30 سیکنڈ سے کم کر کے 1–3 سیکنڈ کر دیتا ہے۔
جدید Android پروجیکٹ میں LeakCanary 2.x انسٹال کرنے کے لیے build.gradle میں ایک لائن درکار ہے۔ لائبریری خودکار ابتدا کے لیے ContentProvider استعمال کرتی ہے — Application کلاس میں ترمیم کرنے یا MainActivity میں کوڈ شامل کرنے کی ضرورت نہیں۔ انحصار صرف debug بلڈز کے لیے شامل کیا جاتا ہے تاکہ ریلیز APK میں اضافی کوڈ نہ ہو۔
// build.gradle (app/module)
dependencies {
// debugImplementation — لائبریری صرف debug بلڈز کے لیے
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
انحصار شامل کرنے اور پروجیکٹ کو دوبارہ بنانے کے بعد، LeakCanary خودکار طور پر ایپ میں ظاہر ہوتا ہے۔ پہلی لانچ پر، لائبریری ایکٹیویشن کی تصدیق کرنے والا ایک سسٹم نوٹیفکیشن دکھاتی ہے۔ تمام پائی گئی لیکیں نوٹیفکیشنز کے طور پر ظاہر ہوتی ہیں — نوٹیفکیشن کو چھونے سے تفصیلی رپورٹ (LeakTrace) والی اسکرین کھلتی ہے۔
اپنی مرضی کے مطابق بنانے کے لیے، آپ اپنا AppWatcherInstaller بنا سکتے ہیں اور پیرامیٹرز کو اوور رائڈ کر سکتے ہیں: GC ٹائم آؤٹ، ٹریک کی جانے والی اشیاء کی اقسام کی فہرست، ڈسک پر ہیپ ڈمپ سیونگ کو فعال کرنا۔ تاہم، 90% پروجیکٹس کے لیے ڈیفالٹ کنفیگریشن بہترین ہے۔
ورژن 2.12 سے شروع ہو کر، LeakCanary ViewModel، کوروٹین اسکوپس اور Compose State اشیاء کی خودکار ٹریکنگ کو سپورٹ کرتا ہے۔ اضافی انحصار کی ضرورت نہیں — لائبریری خودکار طور پر پتہ لگاتی ہے کہ پروجیکٹ میں کون سے Jetpack اجزاء استعمال ہو رہے ہیں اور متعلقہ ڈیٹیکٹرز کو فعال کرتی ہے۔
LeakCanary رپورٹ (LeakTrace) GC Root سے لیک شدہ شے تک ایک کثیر السطور حوالہ جات کا سلسلہ ہے۔ ہر سطر کلاس اور فیلڈ دکھاتی ہے جس کے ذریعے ایک مضبوط حوالہ گزرتا ہے۔ ڈیولپر کو سلسلہ نیچے سے اوپر پڑھنا چاہیے: نیچے کی سطر لیک شدہ شے ہے، اوپر کی سطر داخلے کا نقطہ (GC Root) ہے۔
ایک عام LeakTrace اس طرح دکھتا ہے: GC Root → Application کا جامد فیلڈ → سنگلٹن → کال بیک → Activity۔ اگر ڈیولپر ایسا سلسلہ دیکھتا ہے، تو مسئلہ واضح ہے: سنگلٹن ایک کال بیک رکھتا ہے جس نے Activity کا حوالہ کیپچر کیا ہے۔ حل — سنگلٹن میں مضبوط حوالہ کو کمزور حوالے سے تبدیل کرنا ہے۔
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
اس مثال میں، LeakCanary دکھاتا ہے کہ MainActivity سلسلے کے ذریعے تھامی ہوئی ہے: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → پھر MainActivity۔ this$0 تیر اشارہ کرتا ہے کہ گمنام کلاس MyCallback نے Activity کا بیرونی حوالہ کیپچر کیا ہے۔ حل — کال بیک کو کمزور حوالہ بنانا یا اسے onDestroy میں منسوخ کرنا ہے۔
LeakCanary سلسلے کے ہر عنصر کے لیے لیک کی حیثیت بھی دکھاتا ہے: NO (کوئی لیک نہیں — یہ ایک جڑ عنصر ہے)، YES (شے کو تباہ کیا جانا چاہیے)، UNKNOWN (حیثیت کا تعین نہیں کیا جا سکا)۔ UNKNOWN حیثیت کا مطلب مسئلہ نہیں ہے — یہ ایک درمیانی شے ہے جسے LeakCanary قطعی طور پر درجہ بند نہیں کر سکتا۔
ورژن 1.x سے 2.x میں منتقلی بنیادی تھی: ڈیولپرز نے لائبریری کو شروع سے دوبارہ لکھا، پرانے HAHA تجزیہ کار کو Kotlin میں لکھے گئے اپنے انجن Shark سے بدل دیا۔ Shark کافی تیز ہے، تجزیہ کے لیے کم میموری درکار ہے اور لیک کی بنیادی وجوہات کو زیادہ درست طریقے سے متعین کرتا ہے۔
| پیرامیٹر | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| تجزیہ کار کی زبان | Java (HAHA — Android SDK کا فورک) | Kotlin (Shark — اپنا انجن) |
| سیٹ اپ | Application میں دستی AppWatcher کنفیگریشن | ContentProvider کے ذریعے خودکار |
| رفتار | ہیپ ڈمپ تجزیہ کے لیے 10–30 سیکنڈ | ہیپ ڈمپ تجزیہ کے لیے 1–5 سیکنڈ |
| کارکردگی | تجزیہ کے دوران 10–50 MB RAM لیتا ہے | تجزیہ کے دوران 2–10 MB RAM لیتا ہے |
Shark کا بنیادی فائدہ یہ ہے کہ یہ پورا ہیپ ڈمپ میموری میں لوڈ نہیں کرتا، بلکہ کم سے کم مختص کے ساتھ اس کے حوالہ جات کے گراف کو عبور کرتا ہے۔ یہ LeakCanary 2.x کو تجزیہ کے دوران OutOfMemoryError کے خطرے کے بغیر کم RAM والے آلات پر استعمال کے قابل بناتا ہے۔
ورژن 2.x نے Android Studio Memory Profiler میں بعد کے تجزیہ کے لیے ہیپ ڈمپ برآمد کرنے کی صلاحیت بھی متعارف کروائی۔ ایسا کرنے کے لیے AppWatcher کنفیگریشن میں dumpHeapWhenLeakFound سیٹنگ کو فعال کریں۔
LeakCanary Android میں عام لیک کی کئی اقسام کو مؤثر طریقے سے دریافت کرتا ہے۔ سب سے عام Activity پر جامد حوالہ جات کے ذریعے لیک ہے — ڈیولپرز سنگلٹن میں Activity کے سیاق و سباق کا حوالہ رکھتے ہیں، اور Activity اپنے لائف سائیکل کے ختم ہونے کے بعد GC کے ذریعے جمع نہیں ہو سکتی۔
دوسری سب سے عام قسم غیر رجسٹرڈ سننے والوں کے ذریعے لیک ہے۔ اگر onStart میں registerListener کال کیا گیا تھا لیکن onStop/onDestroy میں unregisterListener کال نہیں کیا گیا، تو سننے والی شے ایکٹویٹی تباہ ہونے کے بعد بھی سسٹم کے ذریعے تھامی رہتی ہے۔ LeakCanary واضح طور پر دکھاتا ہے کہ کون سا سننے والا اور کس سسٹم سروس میں زندہ ہے۔
// عام لیک: سنگلٹن کال بیک میں Activity کیپچر ہوگئی
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // کال بیک پر مضبوط حوالہ
}
fun unregister() {
callback = null // onDestroy میں کال کرنا نہ بھولیں!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // لیمبڈا this کیپچر کرتا ہے
}
// اگر onDestroy میں unregister کال نہ کیا جائے → Activity لیک
}
}
تیسری قسم BackStack میں Fragment کے ذریعے لیک ہے۔ اگر FragmentTransaction.addToBackStack() کو واپس جاتے وقت Fragment ہٹائے بغیر کال کیا جاتا ہے، تو Fragment کی پرانی مثالیں میموری میں رہ جاتی ہیں۔ LeakCanary ڈیولپمنٹ کے ابتدائی مراحل میں ایسی پوشیدہ لیک کا پتہ لگانے میں مدد کرتا ہے۔
ہر دریافت کردہ لیک کے لیے LeakCanary تفصیل اور درستگی کی سفارشات فراہم کرتا ہے۔ ورژن 2.14 نے Android Lint کے ساتھ انضمام شامل کیا — لائبریری CI میں لیک کا پتہ چلنے پر خودکار طور پر ایشو ٹریکر میں کام بنا سکتی ہے۔
اکثر پوچھے گئے سوالات
ہاں، ضرور۔ LeakCanary build.gradle میں debugImplementation کے ذریعے شامل کیا جاتا ہے، جو اسے خودکار طور پر ریلیز بلڈز سے خارج کر دیتا ہے۔ اگر implementation استعمال کیا جائے تو لائبریری ریلیز APK میں شامل ہو جائے گی اور آخری صارفین کو لیک دکھائے گی — یہ ناقابل قبول ہے۔
کارکردگی پر اثر کم سے کم ہے۔ LeakCanary صرف جزو کے onDestroy کے بعد فعال ہوتا ہے اور UI رینڈرنگ یا ٹچ ہینڈلنگ میں مداخلت نہیں کرتا۔ واحد لاگت ایک مختصر جبری GC وقفہ (تقریباً 100 ms) اور لیک ہونے پر ہیپ ڈمپ لکھنا (سیکنڈ کا حصہ) ہے۔
LeakCanary خودکار طور پر ہیپ ڈمپ کو HPROF فارمیٹ میں ایپ فولڈر میں محفوظ کرتا ہے۔ فائل کو Android Studio کے ذریعے برآمد کیا جا سکتا ہے: Device File Explorer → data/data/com.example/files/leakcanary/. اسے دیکھنے کے لیے، Capture → Open Heap Dump کے ذریعے Memory Profiler میں فائل کھولیں۔
ہاں، ورژن 2.12 سے LeakCanary مکمل طور پر Jetpack Compose کو سپورٹ کرتا ہے۔ لائبریری Composition سیاق و سباق اور State اشیاء کو ٹریک کرتی ہے، Composable فنکشنز میں خودکار طور پر لیک کا پتہ لگاتی ہے۔ علیحدہ کنفیگریشن کی ضرورت نہیں — یہ باکس سے باہر کام کرتا ہے۔
غلط مثبت ممکن ہیں لیکن نایاب۔ LeakCanary لیک کا اعلان کرنے سے پہلے تین بار GC کال استعمال کرتا ہے، جو زیادہ تر غلط مثبت کو ختم کرتا ہے۔ اگر آپ کو لگتا ہے کہ کوئی پتہ لگانا غلط مثبت ہے، تو کنفیگریشن میں مخصوص کلاس کے لیے IgnoredReference بنائیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں