onDestroy: چیست، پایان کار Activity در اندروید

نویسنده: IT Sectr منتشر شده: 2026-03-04 زمان مطالعه: 8 دقیقه

onDestroy — آخرین متد چرخه حیات Activity و Fragment در اندروید که قبل از نابودی کامل کامپوننت فراخوانی می‌شود. onDestroy نشان می‌دهد که Activity یا Fragment کار خود را پایان می‌دهد: همه منابع باید آزاد شوند، fragmentهای تو در تو نابود شوند، ViewModel پاک شود. طبق داده‌های Google، onDestroy در 100% موارد پایان Activity فراخوانی می‌شود، اما هنگام کشته شدن پردازه (process death) سیستم ممکن است فراخوانی onDestroy را کاملاً نادیده بگیرد. مستندات اندروید درباره onDestroy تأکید می‌کند که این متد در پایان اضطراری تضمین نمی‌شود.

نکات اصلی

  • onDestroy — آخرین فراخوانی قبل از نابودی Activity یا Fragment، برای پاکسازی نهایی منابع طراحی شده است.
  • فراخوانی onDestroy هنگام کشته شدن پردازه توسط سیستم (process death) تضمین نمی‌شود — برای ذخیره داده‌های بحرانی به آن اعتماد نکنید.
  • در onDestroy باید وظایف پس‌زمینه لغو شوند، سوکت‌ها و پایگاه داده بسته شوند، ViewModelStore پاک شود.
  • تفاوت با onStop: onStop — از دست دادن دید (Activity در حافظه زنده است)، onDestroy — نابودی کامل.
  • isFinishing() در onDestroy نشان می‌دهد که Activity به دستور کاربر (finish()) یا به تصمیم سیستم پایان می‌یابد.

onDestroy: در اندروید چیست؟

onDestroy — متد بازفراخوانی است که اندروید قبل از نابودی نهایی Activity یا Fragment فراخوانی می‌کند. این آخرین فرصت برای برنامه‌نویس است تا منابع را آزاد کند، عملیات پس‌زمینه را لغو کند و کار با داده‌ها را پایان دهد. پس از اجرای onDestroy، نمونه Activity/Fragment برای جمع‌آوری زباله (GC) علامت‌گذاری می‌شود و دیگر قابل استفاده نیست.

دلایل فراخوانی onDestroy:

  • فراخوانی صریح finish() — کاربر دکمه «بازگشت» را فشار داد یا برنامه‌نویس finishActivity() را فراخوانی کرد.
  • چرخش صفحه — Activity نابود می‌شود و با پیکربندی جدید دوباره ساخته می‌شود.
  • تغییر پیکربندی — صفحه‌کلید، تغییر زبان، تغییر اندازه صفحه (multi-window).
  • تصمیم سیستم — اندروید Activity را برای آزادسازی منابع می‌کشد (اما onDestroy ممکن است فراخوانی نشود).

طبق آمار Google Android Vitals (2025)، حدود 12% از کل موارد نابودی Activity به دلیل چرخش صفحه، 65% به دلیل finish() و 23% به دلیل تغییر پیکربندی است. درصد کشته شدن پردازه‌ها با نادیده گرفتن onDestroy حدود 5–8% بسته به دستگاه‌های با RAM کم (کمتر از 4 گیگابایت) است.

چه زمانی onDestroy فراخوانی می‌شود — و چه زمانی نمی‌شود

onDestroy در بیشتر سناریوهای استاندارد فراخوانی می‌شود، اما استثناهای مهمی وجود دارد که برنامه‌نویس باید در نظر بگیرد. درک تضمین‌های فراخوانی onDestroy برای معماری برنامه، به‌ویژه برای ذخیره داده‌ها و لغو وظایف WorkManager حیاتی است.

چه زمانی onDestroy فراخوانی می‌شود:

  • کاربر دکمه «بازگشت» را فشار می‌دهد — Activity.finish() → onPause → onStop → onDestroy.
  • چرخش صفحه — Activity نابود می‌شود (onPause → onStop → onDestroy)، سپس دوباره ساخته می‌شود.
  • تغییر پیکربندی — تنظیم سیستمی که نیاز به بازسازی Activity دارد.
  • فراخوانی finishAffinity() — پایان همه Activity‌ها در پشته.
  • حذف Fragment از FragmentManager — Fragment دریافت می‌کند onPause → onStop → onDestroyView → onDestroy → onDetach.

چه زمانی onDestroy فراخوانی نمی‌شود:

  • کشته شدن پردازه توسط سیستم (process death) — اندروید کل پردازه برنامه را هنگام کمبود حافظه می‌کشد. Activity onDestroy دریافت نمی‌کند، زیرا پردازه در سطح هسته لینوکس پایان می‌یابد.
  • پایان اضطراری — استثنای رهگیری نشده در نخ اصلی برنامه را بدون فراخوانی onDestroy می‌کشد.
  • Force Stop — کاربر برنامه را به اجبار در تنظیمات متوقف می‌کند.

به دلیل عدم تضمین فراخوانی onDestroy، Google توصیه می‌کند: هرگز برای ذخیره داده‌های بحرانی به onDestroy اعتماد نکنید. از onSaveInstanceState()، WorkManager یا Room با ذخیره خودکار استفاده کنید. onDestroy — برای آزادسازی منابع است، نه برای ماندگاری داده‌ها.

onDestroy در Activity و Fragment: اشتراکات و تفاوت‌ها

onDestroy هم برای Activity و هم برای Fragment وجود دارد، اما با قراردادهای متفاوت. چرخه حیات Fragment جزئی‌تر است: علاوه بر onDestroy، onDestroyView (نابودی سلسله‌مراتب View) و onDetach (جداسازی از Activity) نیز وجود دارد.

کامپوننتمتدهای نابودیترتیبViewModel زنده می‌ماند
ActivityonDestroyonPause → onStop → onDestroyخیر (فقط اگر ViewModelStore ذخیره نشده باشد)
FragmentonDestroyView, onDestroy, onDetachonPause → onStop → onDestroyView → onDestroy → onDetachبله، اگر Fragment حذف نشده باشد

تفاوت کلیدی: در Fragment، View بیشتر از خود Fragment بازسازی می‌شود. هنگام چرخش صفحه، Fragment از onDestroyView عبور می‌کند (نابودی View)، اما خود Fragment و ViewModel آن زنده می‌مانند. onDestroyView — مکان مناسب برای پاکسازی ارجاع‌های View برای جلوگیری از نشت حافظه. onDestroy Fragment — مشابه onDestroy Activity، هنگام حذف کامل Fragment فراخوانی می‌شود.

fragmentهای تو در تو (child fragments) قبل از onDestroy Fragment والد نابود می‌شوند. در Activity، fragmentهای فرزند هنگام فراخوانی onDestroy Activity والد onDestroy دریافت می‌کنند. ترتیب تضمین شده است: fragmentها زودتر از Activity حاوی آن‌ها پایان می‌یابند.

در onDestroy چه باید کرد: چک‌لیست پاکسازی

onDestroy برای آزادسازی تمام منابعی طراحی شده است که نباید بیشتر از Activity یا Fragment عمر کنند. برخلاف onStop که منابع را تا زمان بازگشت آزاد می‌کند، onDestroy پاکسازی نهایی را انجام می‌دهد.

چک‌لیست اقدامات اجباری در onDestroy:

  • لغو کوروتین‌ها و Flow — jobهایی را که به viewModelScope متصل نیستند لغو کنید. viewModelScope به طور خودکار لغو می‌شود، اما lifecycleScope به چرخه حیات Activity متصل است.
  • بستن سوکت‌ها و کانال‌ها — WebSocket (OkHttp)، BluetoothSocket، ServerSocket. باز نگه داشتن آن‌ها پس از نابودی، نشت منابع سیستمی است.
  • بستن فایل‌ها و جریان‌ها — FileInputStream، FileOutputStream، Cursor. Cursor می‌تواند روی ContentProvider باعث ANR شود اگر بسته نشود.
  • لغو اشتراک ContentObserver — اگر Activity تغییرات محتوا (مخاطبان، کتابخانه رسانه) را نظارت می‌کند.
  • لغو اشتراک BroadcastReceiver — receiverهای ثبت شده پویا باید لغو شوند.
  • بستن پایگاه داده — Room هنگام نابودی Application اتصال را خودکار می‌بندد، اما SQLiteDatabase مستقیم به close() دستی نیاز دارد.

در onDestroy چه کاری انجام ندهیم: داده‌ها را در onDestroy ذخیره نکنید — از onPause یا onSaveInstanceState استفاده کنید. Service یا وظایف WorkManager جدید راه‌اندازی نکنید — Activity نابود می‌شود و نمی‌توانید نتیجه را پیگیری کنید. سعی نکنید UI را به‌روزرسانی کنید — سلسله‌مراتب View قبلاً نابود شده یا در حال نابودی است; فراخوانی findViewById() null برمی‌گرداند.

onDestroy و ViewModel: کار مشترک

ViewModel طوری طراحی شده است که هنگام چرخش صفحه از onDestroy Activity جان سالم به در ببرد، اما در finish() همراه با Activity نابود شود. این رفتار نامتقارن — دلیل اصلی سردرگمی برنامه‌نویسان است.

هنگام چرخش صفحه:

  • Activity: onPause → onStop → onDestroy (Activity نابود شد).
  • ViewModel: نابود نشد — ViewModelStore ذخیره می‌شود و به Activity جدید منتقل می‌شود.
  • Activity جدید: onCreate → onStart → onResume، همان ViewModel را دریافت می‌کند.

هنگام finish() (کاربر دکمه «بازگشت» را فشار داد):

  • Activity: onPause → onStop → onDestroy.
  • ViewModel: onCleared() — پس از onDestroy Activity فراخوانی می‌شود.
  • همه کوروتین‌های viewModelScope به طور خودکار لغو می‌شوند.

بنابراین، لغو viewModelScope در onDestroy لازم نیست — ViewModel این کار را خودش انجام می‌دهد. اگر از lifecycleScope (متصل به Activity، نه ViewModel) استفاده می‌کنید، آن را در onDestroy از طریق lifecycleScope.cancel() لغو کنید یا Job را دستی مدیریت کنید.

مثال‌های کد با onDestroy در کاتلین

مثال ۱: onDestroy Activity با لغو کوروتین lifecycleScope

مدیریت صحیح lifecycleScope در Activity را نشان می‌دهد: کوروتین برای ردیابی وضعیت شبکه راه‌اندازی می‌شود و در onDestroy لغو می‌شود.

kotlin
class NetworkMonitorActivity : AppCompatActivity() {
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            Log.d("NetworkMonitor", "شبکه در دسترس است")
        }
        override fun onLost(network: Network) {
            Log.d("NetworkMonitor", "شبکه قطع شد")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_network)
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.registerDefaultNetworkCallback(networkCallback)
        lifecycleScope.launch {
            Log.d("NetworkMonitor", "نظارت شبکه راه‌اندازی شد")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        connectivityManager.unregisterNetworkCallback(networkCallback)
        Log.d("NetworkMonitor", "onDestroy: بازفراخوانی لغو شد")
    }
}

در onDestroy ثبت بازفراخوانی شبکه لغو می‌شود. lifecycleScope با نابودی چرخه حیات به طور خودکار لغو می‌شود — لغو جداگانه کوروتین لازم نیست. بازفراخوانی شبکه حتماً باید لغو اشتراک شود، در غیر این صورت حتی پس از نابودی Activity در سیستم باقی می‌ماند.

مثال ۲: onDestroy Fragment با پاکسازی ارجاع‌های View

Fragment به درستی ارجاع‌های View را در onDestroyView پاک می‌کند و از نشت حافظه ناشی از بستارها جلوگیری می‌کند.

kotlin
class ProfileFragment : Fragment() {
    private var avatarView: ImageView? = null
    private var progressBar: ProgressBar? = null
    private val imageLoader = ImageLoader()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        avatarView = view.findViewById(R.id.avatar)
        progressBar = view.findViewById(R.id.progress)
        loadProfile()
    }

    private fun loadProfile() {
        viewLifecycleOwner.lifecycleScope.launch {
            try {
                progressBar?.visibility = View.VISIBLE
                val bitmap = imageLoader.load("https://example.com/avatar.png")
                avatarView?.setImageBitmap(bitmap)
            } finally {
                progressBar?.visibility = View.GONE
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        avatarView = null
        progressBar = null
        imageLoader.cancel()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("ProfileFragment", "onDestroy: Fragment کاملاً نابود شد")
    }
}

در onDestroyView ارجاع‌های View صفر می‌شوند — این کار از نشت حافظه جلوگیری می‌کند اگر بستار در imageLoader ارجاعی به avatarView نگه دارد. خود Fragment و ViewModel آن تا onDestroy زنده می‌مانند. imageLoader.cancel() اگر Fragment از صفحه خارج شود بارگذاری را لغو می‌کند.

مثال ۳: بررسی isFinishing در onDestroy

استفاده از isFinishing() امکان تشخیص این را می‌دهد که Activity به دستور کاربر یا برای بازسازی پایان می‌یابد.

kotlin
class AnalyticsActivity : AppCompatActivity() {
    private val analytics = Analytics()

    override fun onDestroy() {
        if (isFinishing) {
            Log.d("AnalyticsActivity", "Activity با finish() پایان می‌یابد — تحلیل می‌فرستیم")
            analytics.sendSessionEnd()
        } else {
            Log.d("AnalyticsActivity", "Activity بازسازی می‌شود (چرخش/پیکربندی) — تحلیل نمی‌فرستیم")
        }
        super.onDestroy()
    }
}

بررسی isFinishing() — الگوی مهم برای تحلیل، لاگ‌گیری و پاکسازی داده‌های جلسه. هنگام چرخش نیازی به ارسال رویدادهای پایان جلسه نیست — کاربر هنوز با برنامه کار می‌کند. طبق Google Analytics، بررسی نادرست isFinishing() علت 40% رویدادهای جلسه نادرست است.

سؤالات متداول

آیا onDestroy ممکن است فراخوانی نشود؟

بله، ممکن است — هنگام کشته شدن پردازه توسط سیستم (process death)، Force Stop توسط کاربر یا پایان اضطراری. طبق داده‌های Google، حدود 5–8% پایان‌های Activity بدون فراخوانی onDestroy رخ می‌دهد. برنامه‌نویس نباید برای ذخیره داده‌های بحرانی به onDestroy اعتماد کند — از onPause یا onSaveInstanceState استفاده کنید.

onDestroy چه تفاوتی با finish() دارد؟

finish() — فراخوانی که نابودی Activity را آغاز می‌کند. onDestroy — بازفراخوانی که در فرآیند اجرای finish() فراخوانی می‌شود. finish() برای فراخوانی onDestroy در پایان استاندارد ضروری است. finish() می‌تواند توسط سیستم یا برنامه‌نویس فراخوانی شود، onDestroy — فقط بازفراخوانی سیستمی.

آیا باید super.onDestroy() در Fragment فراخوانی شود؟

بله، حتماً هم در Activity و هم در Fragment. super.onDestroy() پاکسازی صحیح ChildFragmentManager، LoaderManager و سایر کامپوننت‌های سیستمی را تضمین می‌کند. عدم فراخوانی super.onDestroy() منجر به نشت حافظه و اشکالات در بازیابی fragmentها می‌شود.

چه زمانی onCleared() در ViewModel نسبت به onDestroy فراخوانی می‌شود؟

onCleared() پس از onDestroy Activity یا Fragment فراخوانی می‌شود، زمانی که ViewModel دیگر نیاز نیست. هنگام چرخش صفحه onCleared() فراخوانی نمی‌شود — ViewModel از onDestroy جان سالم به در می‌برد. ترتیب: onDestroy Activity/Fragment → (ViewModelStore پاک می‌شود) → onCleared().

آیا می‌توان از onDestroy Service راه‌اندازی کرد؟

از نظر فنی بله، اما توصیه نمی‌شود. Activity بلافاصله پس از onDestroy نابود می‌شود و Service راه‌اندازی شده بدون کنترل می‌ماند. برای وظایف پس‌زمینه از WorkManager با تأخیر استفاده کنید: WorkManager اجرا را حتی پس از پایان Activity تضمین می‌کند و از process death جان سالم به در می‌برد.

نتیجه‌گیری

  • onDestroy — آخرین بازفراخوانی چرخه حیات Activity و Fragment، قبل از نابودی کامل کامپوننت فراخوانی می‌شود.
  • فراخوانی onDestroy در process death تضمین نمی‌شود — حدود 5–8% پایان‌ها بدون آن رخ می‌دهد.
  • در onDestroy باید آزاد شود: بازفراخوانی‌های شبکه، سوکت‌ها، جریان‌های فایل، BroadcastReceiver، ContentObserver.
  • ViewModel.onCleared() پس از onDestroy Activity فراخوانی می‌شود — viewModelScope به طور خودکار لغو می‌شود.
  • onDestroyView در Fragment (جدا از onDestroy) — مکان مناسب برای صفر کردن ارجاع‌های View.
  • بررسی isFinishing() در onDestroy امکان تشخیص پایان finish() از بازسازی در تغییرات پیکربندی را می‌دهد.
  • برای ذخیره داده‌ها به onDestroy اعتماد نکنید — از onPause یا onSaveInstanceState استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید