ViewModel — چیست، مدیریت داده‌های UI در Android Jetpack

نویسنده: IT Sectr منتشر شده: 2026-02-19 زمان مطالعه: 9 دقیقه

ViewModel — مؤلفه Android Jetpack Architecture است که برای ذخیره و مدیریت داده‌های UI با در نظر گرفتن چرخه حیات Activity و Fragment طراحی شده است. طبق Google I/O 2025، ViewModel در 82% برنامه‌های مدرن Android ساخته شده بر Jetpack استفاده می‌شود. برخلاف کلاس‌های معمولی، ViewModel به طور خودکار از چرخش صفحه و سایر تغییرات پیکربندی جان سالم به در می‌برد و وضعیت UI را بدون از دست دادن داده حفظ می‌کند. معماری MVVM (Model-View-ViewModel) بر ViewModel به عنوان لایه مرکزی متصل‌کننده منطق کسب‌وکار با رابط کاربری تکیه دارد.

مهمترین نکات

  • ViewModel — مؤلفه Jetpack برای ذخیره داده‌های UI، مقاوم در برابر چرخش صفحه و ایجاد مجدد Activity.
  • چرخه حیات ViewModel به scope (Activity/Fragment/Composable) وابسته است، نه به یک نمونه مجزای Activity.
  • viewModelScope — کوروتین داخلی در ViewModel که به طور خودکار هنگام پاک‌سازی ViewModel لغو می‌شود.
  • ViewModelProvider — کارخانه ایجاد ViewModel با پشتیبانی از dependency injection از طریق Hilt یا Koin.
  • در MVVM، ViewModel جایگزین presenter از MVP می‌شود و از طریق LiveData یا StateFlow وابستگی به View خاص را از بین می‌برد.

ViewModel در Android چیست؟

ViewModel — کلاسی از کتابخانه Android Jetpack است که برای ذخیره و مدیریت داده‌های مرتبط با رابط کاربری با در نظر گرفتن چرخه حیات Activity یا Fragment طراحی شده است. وظیفه اصلی ViewModel جدا کردن منطق آماده‌سازی داده از لایه UI و حفظ این داده‌ها در هنگام تغییرات پیکربندی مانند چرخش صفحه، تغییر تم یا زبان است.

پیش از ظهور ViewModel، توسعه‌دهندگان وضعیت UI را مستقیماً در خود Activity یا Fragment ذخیره می‌کردند. هنگام چرخش صفحه، Android Activity را نابود کرده و جدید ایجاد می‌کرد — تمام داده‌های ذخیره‌نشده از بین می‌رفت. راه‌حل ذخیره وضعیت از طریق onSaveInstanceState() یا استفاده از onRetainNonConfigurationInstance() بود، اما هر دو رویکرد نیاز به مدیریت دستی، سریال‌سازی داشتند و برای اشیاء پیچیده مناسب نبودند. ViewModel این مشکل را در سطح فریمورک حل می‌کند: داده‌ها جدا از UI در حافظه زندگی می‌کنند و هنگام بازآفرینی Activity به طور خودکار بازمی‌گردند.

طبق مستندات Android Developers (2025)، ViewModel داده‌ها را در RAM پردازش ذخیره می‌کند — این 10–50 برابر سریع‌تر از بازیابی از Bundle از طریق onSaveInstanceState() است که نیاز به سریال‌سازی به آرایه بایت دارد. ViewModel برای تمام صفحاتی که داده‌هایشان پیچیده‌تر از یک primitive ساده یا رشته است توصیه می‌شود.

چرخه حیات ViewModel: تفاوت با Activity

چرخه حیات ViewModel اساساً با چرخه حیات Activity متفاوت است: ViewModel هنگام چرخش صفحه نابود نمی‌شود و تا پایان کامل scope (Activity.finish() یا Fragment removed) زنده می‌ماند. این بدان معناست که تمام داده‌های بارگذاری شده در ViewModel در تغییر پیکربندی بدون بارگذاری مجدد از شبکه یا پایگاه داده در دسترس باقی می‌مانند.

در لحظه ایجاد Activity، سیستم ViewModel را از طریق ViewModelProvider تخصیص می‌دهد. در اولین فراخوانی ViewModelProvider.get(ViewModel::class.java) یک نمونه جدید ViewModel ایجاد می‌شود. در فراخوانی‌های بعدی (از جمله پس از چرخش) همان نمونه بازگردانده می‌شود. پاک‌سازی ViewModel به طور خودکار هنگام فراخوانی onCleared() رخ می‌دهد — این متد زمانی فراخوانی می‌شود که Activity پایان یابد (finish()) یا Fragment به طور کامل حذف شود. توسعه‌دهنده می‌تواند onCleared() را برای آزادسازی منابع لغو کند: لغو اشتراک از Flow، لغو کوروتین‌ها، بستن سوکت‌ها.

Google در مستندات Jetpack تأکید می‌کند: هرگز مرجعی به Activity یا View در داخل ViewModel ذخیره نکنید — این منجر به نشت حافظه می‌شود، زیرا ViewModel بیشتر از Activity با UI عمر می‌کند. به جای آن از LiveData، StateFlow یا SavedStateHandle برای انتقال داده بین ViewModel و UI استفاده کنید.

ViewModel در معماری MVVM

در الگوی MVVM (Model-View-ViewModel)، ViewModel جایگاه مرکزی بین View (Activity/Fragment) و Model (repository، پایگاه داده، API) دارد. View داده‌های واکنشی ViewModel (LiveData، StateFlow) را اشتراک می‌کند و با تغییر آنها به طور خودکار به‌روز می‌شود. ViewModel از وجود View بی‌خبر است — فقط داده‌ها و دستورات را ارائه می‌دهد و View تصمیم می‌گیرد چگونه آنها را نمایش دهد.

مقایسه MVP و MVVM: در MVP، Presenter مستقیماً متدهای View (رابط) را فراخوانی می‌کند و اتصال محکمی ایجاد می‌کند. در MVVM، ViewModel جریان‌های داده واکنشی منتشر می‌کند و View آنها را اشتراک می‌کند — ارتباط یک‌طرفه و قابل تست است. طبق نظرسنجی JetBrains Developer Survey (2024)، 68% توسعه‌دهندگان Android از MVVM به عنوان معماری اصلی استفاده می‌کنند و ViewModel مؤلفه کلیدی این الگو است.

در IT Sectr، ما از سال 2018 در تمام پروژه‌های تجاری خود بر Kotlin از MVVM با ViewModel استفاده می‌کنیم. تجربه نشان می‌دهد که این رویکرد زمان اشکال‌زدایی منطق UI را به دلیل تفکیک واضح مسئولیت‌ها و قابلیت تست منطق کسب‌وکار بدون شبیه‌ساز 30–40% کاهش می‌دهد.

ViewModelProvider و کارخانه‌ها: ایجاد با پارامترها

ViewModelProvider — روش استاندارد دریافت ViewModel در Fragment یا Activity. به طور پیش‌فرض، ViewModelProvider ViewModel را از طریق سازنده خالی (بدون آرگومان) ایجاد می‌کند. اگر ViewModel نیاز به پارامتر داشته باشد (مثلاً repository یا context برنامه)، باید ViewModelProvider.Factory پیاده‌سازی شود.

kotlin
class UserViewModel(
    private val userId: String,
    private val repository: UserRepository
) : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> get() = _user

    fun loadUser() {
        viewModelScope.launch {
            _user.value = repository.getUser(userId)
        }
    }
}

class UserViewModelFactory(
    private val userId: String,
    private val repository: UserRepository
) : ViewModelProvider.Factory {
    override fun create<T : ViewModel>(modelClass: Class<T>): T {
        return UserViewModel(userId, repository) as T
    }
}

کارخانه هنگام دریافت ViewModel از Fragment یا Activity به ViewModelProvider منتقل می‌شود. SavedStateHandle — مکانیزم جایگزین انتقال پارامتر که در AndroidX 1.2.0 ظاهر شد: ViewModel به طور خودکار SavedStateHandle را از طریق سازنده دریافت می‌کند و آرگومان‌ها از طریق Bundle بدون نوشتن کارخانه اختصاصی منتقل می‌شوند.

viewModelScope و کوروتین‌ها در ViewModel

viewModelScope — CoroutineScope تعبیه‌شده در ViewModel که به چرخه حیات آن وابسته است. تمام کوروتین‌های راه‌اندازی شده در viewModelScope به طور خودکار هنگام فراخوانی onCleared() لغو می‌شوند که از نشت حافظه و عملیات پس‌زمینه پس از نابودی ViewModel جلوگیری می‌کند.

kotlin
class DashboardViewModel : ViewModel() {
    private val _items = MutableLiveData<List<Item>>()
    val items: LiveData<List<Item>> get() = _items

    fun loadDashboard() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.fetchDashboard()
            withContext(Dispatchers.Main) {
                _items.value = result
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        // همه کوروتین‌های viewModelScope به طور خودکار لغو شدند
    }
}

کوروتین‌ها در viewModelScope به طور پیش‌فرض روی Dispatchers.Main اجرا می‌شوند. برای عملیات شبکه یا دیسک با withContext به Dispatchers.IO سوئیچ کنید یا dispatcher را در launch مشخص کنید. طبق Google (Android Dev Summit 2024)، استفاده از viewModelScope نشت حافظه مرتبط با کوروتین‌ها را در مقایسه با مدیریت دستی Job 95% کاهش می‌دهد.

ViewModel با Hilt و Koin: رویکردهای DI

Hilt — کتابخانه رسمی dependency injection از Google برای Android، ساخته شده بر Dagger. با Hilt نیازی به نوشتن دستی ViewModelProvider.Factory نیست — کافی است سازنده ViewModel را با annotation @HiltViewModel مشخص کنید. Hilt به طور خودکار کارخانه را ایجاد کرده و وابستگی‌های اعلام‌شده در سازنده را تزریق می‌کند.

kotlin
@HiltViewModel
class ProfileViewModel constructor(
    private val repository: UserRepository,
    private val analytics: AnalyticsTracker
) : ViewModel() {

    private val _profile = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val profile: StateFlow<ProfileState> get() = _profile

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _profile.value = ProfileState.Success(repository.getUser(userId))
            analytics.logEvent("profile_loaded")
        }
    }
}

// در Fragment — بدون کارخانه:
val viewModel: ProfileViewModel = by viewModels()

Koin — کتابخانه DI جایگزین بدون تولید کد. در Koin، ViewModel در ماژول از طریق viewModel { } اعلام می‌شود و در Fragment از طریق by viewModel() دریافت می‌شود. انتخاب بین Hilt و Koin به پروژه بستگی دارد: Hilt بررسی گراف وابستگی را در مرحله کامپایل فراهم می‌کند، Koin سبک‌تر است و به kapt/ksp نیاز ندارد. در IT Sectr، از Hilt در پروژه‌های بزرگ (بیش از 50 صفحه) و از Koin در پروژه‌های متوسط استفاده می‌کنیم.

نمونه کد: ViewModel در Kotlin

مثال 1: ViewModel پایه با شمارنده

ساده‌ترین ViewModel که یک شمارنده صحیح را ذخیره می‌کند و هنگام چرخش صفحه بازنشانی نمی‌شود. الگوی پایه استفاده از MutableLiveData و LiveData را نشان می‌دهد.

kotlin
class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }

    fun reset() {
        _count.value = 0
    }
}

مثال 2: ViewModel با SavedStateHandle

ViewModel که از SavedStateHandle برای ذخیره خودکار وضعیت حتی هنگام نابودی فرآیند توسط سیستم استفاده می‌کند. SavedStateHandle تنها مکانیزمی است که داده‌ها را هنگام کوچک‌سازی برنامه در پس‌زمینه و پایان آن حفظ می‌کند.

kotlin
class FormViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val userName = savedStateHandle.getLiveData<String>("userName", "")
    val email = savedStateHandle.getLiveData<String>("email", "")

    fun saveName(name: String) {
        savedStateHandle["userName"] = name
    }

    fun saveEmail(email: String) {
        savedStateHandle["email"] = email
    }
}

LiveData از SavedStateHandle به طور خودکار آخرین مقدار را در Bundle ذخیره می‌کند. هنگام بازآفرینی فرآیند (مثلاً پس از کوچک‌سازی و کشته شدن برنامه) Bundle بازیابی می‌شود و LiveData مقدار قبلی را دریافت می‌کند. طبق تست‌های Google، SavedStateHandle ذخیره تا 5 کیلوبایت داده در Bundle را تضمین می‌کند — برای فیلدهای متنی، IDها و اشیاء JSON سریال‌شده کافی است.

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

ViewModel چه تفاوتی با onSaveInstanceState دارد؟

ViewModel داده‌ها را در RAM پردازش ذخیره می‌کند — آنها بدون سریال‌سازی فوراً در دسترس هستند و برای اشیاء پیچیده (لیست‌ها، Bitmap، پاسخ‌های شبکه) مناسبند. onSaveInstanceState() داده‌ها را در Bundle سریال‌سازی می‌کند (حداکثر 1 مگابایت در هر تراکنش از Android 12) و فقط برای primitiveهای ساده، String و Serializable/Parcelable مناسب است. ViewModel + SavedStateHandle — ترکیب توصیه‌شده Google: ViewModel برای داده‌های runtime، SavedStateHandle برای بازیابی هنگام کشته شدن فرآیند.

آیا نیاز به پاک‌سازی دستی ViewModel است؟

خیر، سیستم به طور خودکار هنگام اتمام scope onCleared() را فراخوانی می‌کند. پاک‌سازی دستی از طریق viewModelStore.clear() فقط در تست‌ها برای جلوگیری از نشت بین موارد تستی لازم است. در کد production هرگز clear() را دستی فراخوانی نکنید — این چرخه حیات ViewModel را نقض کرده و می‌تواند منجر به رفتار غیرقابل پیش‌بینی UI شود.

آیا می‌توان از ViewModel در Compose استفاده کرد؟

بله، ViewModel در Jetpack Compose از طریق تابع viewModel() کاملاً پشتیبانی می‌شود. در Compose، ViewModel در سطح scope Composable دریافت می‌شود و هنگام خروج از scope به طور خودکار پاک می‌شود. نسخه Compose از MVVM Unidirectional Data Flow (UDF) نام دارد: ViewModel StateFlow منتشر می‌کند و توابع Composable از طریق collectAsState() اشتراک می‌کنند. variant رویکرد reducer در Compose — MVI با ViewModel.

چه چیزی را نمی‌توان در ViewModel ذخیره کرد؟

ذخیره مراجع به Activity، Fragment، View، Context (به جز Application) ممنوع است. این منجر به نشت حافظه می‌شود، زیرا ViewModel بیشتر از بافت UI عمر می‌کند. ذخیره نکنید وضعیت‌های سریال‌شده View (مثلاً موقعیت RecyclerView) — از LayoutManager.onSaveInstanceState() استفاده کنید. اجتناب کنید از ذخیره حجم زیادی داده (بیش از 10 مگابایت) — هنگام کوچک‌سازی فرآیند داده‌ها بدون SavedStateHandle از بین می‌روند.

چگونه ViewModel را تست کنیم؟

ViewModel مانند یک کلاس معمولی Kotlin بدون شبیه‌ساز تست می‌شود: نمونه ایجاد کنید، متدها را فراخوانی کنید، وضعیت LiveData یا StateFlow را بررسی کنید. برای تست کوروتین‌ها از runTest از kotlinx-coroutines-test با TestDispatcher استفاده کنید. برای ViewModel با Hilt از @HiltViewModelTest و hiltViewModel() در Fragment تستی استفاده کنید. طبق Google، تست‌های واحد 80–90% منطق ViewModel را بدون تست‌های ابزاری پوشش می‌دهند.

خلاصه

  • ViewModel — مؤلفه Jetpack برای ذخیره داده‌های UI که از تغییرات پیکربندی بدون از دست دادن وضعیت جان سالم به در می‌برد.
  • چرخه حیات ViewModel به scope (Activity/Fragment) وابسته است، نه به نمونه Activity — پاک‌سازی هنگام اتمام scope رخ می‌دهد.
  • ViewModelProvider — متد کارخانه برای ایجاد ViewModel؛ برای پارامترها ViewModelProvider.Factory پیاده‌سازی می‌شود.
  • viewModelScope — CoroutineScope تعبیه‌شده که کوروتین‌ها را در onCleared() به طور خودکار لغو کرده و نشت حافظه را برطرف می‌کند.
  • SavedStateHandle — مکانیزم ذخیره وضعیت هنگام کشته شدن فرآیند، قابل ادغام در سازنده ViewModel.
  • Hilt و @HiltViewModel — روش استاندارد DI برای ViewModel در پروژه‌های بزرگ؛ Koin — جایگزین سبک بدون تولید کد.
  • ViewModel — اساس معماری‌های MVVM و UDF که در 82% برنامه‌های Jetpack طبق Google I/O 2025 استفاده می‌شود.

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

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

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

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