نمای کلی ViewModel بخشی از Android Jetpack .
کلاس ViewModel یک نگهدارندهی وضعیت در سطح صفحه یا منطق کسب و کار است. این کلاس وضعیت را در اختیار رابط کاربری قرار میدهد و منطق کسب و کار مرتبط را کپسولهسازی میکند. مزیت اصلی آن این است که وضعیت را ذخیره کرده و آن را در طول تغییرات پیکربندی حفظ میکند. این بدان معناست که رابط کاربری شما هنگام پیمایش بین فعالیتها یا دنبال کردن تغییرات پیکربندی، مانند هنگام چرخاندن صفحه، نیازی به واکشی مجدد دادهها ندارد.
برای اطلاعات بیشتر در مورد دارندگان وضعیت، به راهنمای دارندگان وضعیت مراجعه کنید. به طور مشابه، برای اطلاعات بیشتر در مورد لایه رابط کاربری به طور کلی، به راهنمای لایه رابط کاربری مراجعه کنید.
مزایای ViewModel
جایگزین ViewModel، یک کلاس ساده است که دادههایی را که در رابط کاربری خود نمایش میدهید، در خود نگه میدارد. این میتواند هنگام پیمایش بین فعالیتها یا مقاصد ناوبری مشکلساز شود. اگر این کار را با استفاده از مکانیسم وضعیت نمونه ذخیره شده ذخیره نکنید، آن دادهها از بین میروند. ViewModel یک API مناسب برای ماندگاری دادهها ارائه میدهد که این مشکل را حل میکند.
از طرف دیگر، برای دارندگان وضعیت خالص، Compose قابلیتهای retain را ارائه میدهد که به کلاسهای ساده اجازه میدهد بدون زیرساخت کامل یک ViewModel، از تغییرات پیکربندی جان سالم به در ببرند. در حالی که هر دو مکانیسم به حفظ وضعیت کمک میکنند، اما به طور کلی ایمنتر است که یک ViewModel را به یک نمونه حفظ شده ارائه دهید تا برعکس، زیرا چرخه عمر و رفتارهای پاکسازی آنها متفاوت است.
مزایای کلیدی کلاس ViewModel اساساً دو مورد است:
- به شما امکان میدهد حالت رابط کاربری (UI) را ثابت نگه دارید.
- این امکان دسترسی به منطق کسب و کار را فراهم میکند.
پشتکار
ViewModel امکان ماندگاری را هم از طریق حالتی که ViewModel نگه میدارد و هم از طریق عملیاتی که ViewModel اجرا میکند، فراهم میکند. این ذخیرهسازی به این معنی است که شما مجبور نیستید دادهها را از طریق تغییرات پیکربندی رایج، مانند چرخش صفحه، دوباره دریافت کنید.
دامنه
وقتی یک ViewModel را نمونهسازی میکنید، به آن شیءای میدهید که رابط ViewModelStoreOwner را پیادهسازی میکند. این میتواند یک مقصد ناوبری، نمودار ناوبری، فعالیت یا هر نوع دیگری باشد که رابط را پیادهسازی میکند. همچنین میتوانید با استفاده از API rememberViewModelStoreOwner ، مستقیماً یک ViewModel را به یک composable محدود کنید. سپس ViewModel شما به چرخه عمر ViewModelStoreOwner محدود میشود. این ViewModel تا زمانی که ViewModelStoreOwner آن به طور دائم از بین برود (مانند زمانی که مالک composable از Composition خارج میشود)، در حافظه باقی میماند.
طیف وسیعی از کلاسها، زیرکلاسهای مستقیم یا غیرمستقیم رابط ViewModelStoreOwner هستند. زیرکلاسهای مستقیم عبارتند از ComponentActivity و NavBackStackEntry . برای مشاهده لیست کامل زیرکلاسهای غیرمستقیم، به مرجع ViewModelStoreOwner مراجعه کنید. برای اینکه ViewModelها را به آیتمهای منفرد در یک LazyList یا Pager محدود کنید، rememberViewModelStoreProvider() برای انتقال مدیریت مالک به والد استفاده کنید.
وقتی پیکربندی اکتیویتی میزبان تغییر میکند، کار ناهمزمان در ViewModel ادامه مییابد، چه در محدوده اکتیویتی باشد و چه در محدوده یک کامپوزبل خاص. این کلید ماندگاری است.
برای اطلاعات بیشتر، به بخش چرخه عمر ViewModel که در ادامه میآید، رابطهای برنامهنویسی کاربردی ViewModel Scoping و راهنمای مربوط به انتقال وضعیت برای Jetpack Compose مراجعه کنید.
دستگیره وضعیت ذخیره شده
SavedStateHandle به شما امکان میدهد دادهها را نه تنها در طول تغییرات پیکربندی، بلکه در طول مرگ فرآیند نیز حفظ کنید. به عبارت دیگر، به شما امکان میدهد وضعیت رابط کاربری را حتی زمانی که کاربر برنامه را میبندد و بعداً آن را باز میکند، دست نخورده نگه دارید.
برای اطلاعات بیشتر در مورد ذخیره وضعیت رابط کاربری، به «ذخیره وضعیت رابط کاربری در نوشتن» مراجعه کنید.
دسترسی به منطق کسب و کار
اگرچه بخش عمدهای از منطق کسبوکار در لایه دادهها وجود دارد، لایه رابط کاربری نیز میتواند شامل منطق کسبوکار باشد. این میتواند زمانی اتفاق بیفتد که دادهها را از چندین مخزن برای ایجاد حالت رابط کاربری صفحه ترکیب میکنیم، یا زمانی که نوع خاصی از دادهها به لایه داده نیاز ندارند.
ViewModel مکان مناسبی برای مدیریت منطق کسب و کار در لایه رابط کاربری است. ViewModel همچنین مسئول مدیریت رویدادها و واگذاری آنها به لایههای دیگر سلسله مراتب است، زمانی که منطق کسب و کار برای تغییر دادههای برنامه نیاز به اعمال دارد.
پیادهسازی یک ViewModel
در ادامه، نمونهای از پیادهسازی ViewModel برای صفحهای که به کاربر اجازه میدهد تاس بیندازد، آورده شده است.
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
// Expose screen UI state
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Handle business logic
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
سپس میتوانید به ViewModel از یک صفحه نمایش قابل ترکیب به صورت زیر دسترسی داشته باشید:
import androidx.lifecycle.viewmodel.compose.viewModel
// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
viewModel: DiceRollViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// Update UI elements
}
استفاده از کوروتینها با ViewModel
ViewModel از کوروتینهای کاتلین پشتیبانی میکند. این قابلیت را دارد که کار ناهمزمان را به همان روشی که حالت رابط کاربری را حفظ میکند، حفظ کند.
برای اطلاعات بیشتر، به «استفاده از کوروتینهای کاتلین با کامپوننتهای معماری اندروید» مراجعه کنید.
چرخه حیات یک ViewModel
چرخه حیات یک ViewModel مستقیماً به محدوده آن گره خورده است. یک ViewModel تا زمانی که ViewModelStoreOwner که محدوده آن ناپدید شود، در حافظه باقی میماند. این ممکن است در زمینههای زیر رخ دهد:
- در مورد یک فعالیت، زمانی که به پایان میرسد.
- در مورد ورودی ناوبری، وقتی از پشته پشتی حذف میشود.
- در مورد یک composable، وقتی از Composition خارج میشود. میتوانید
rememberViewModelStoreOwnerبرای دسترسی مستقیم به یک ViewModel در بخش دلخواهی از رابط کاربری خود (مانندPagerیاLazyList) استفاده کنید.
این امر ViewModelها را به یک راهحل عالی برای ذخیره دادههایی تبدیل میکند که از تغییرات پیکربندی جان سالم به در میبرند.
شکل ۱ حالتهای مختلف چرخه حیات یک فعالیت را در حین چرخش و سپس پایان آن نشان میدهد. این تصویر همچنین طول عمر ViewModel را در کنار چرخه حیات فعالیت مرتبط نشان میدهد. این نمودار خاص، حالتهای یک فعالیت را نشان میدهد.

شما معمولاً اولین باری که سیستم متد onCreate() یک شیء فعالیت را فراخوانی میکند، یک ViewModel درخواست میکنید. سیستم ممکن است onCreate() را چندین بار در طول وجود یک فعالیت فراخوانی کند، مانند زمانی که صفحه نمایش دستگاه چرخانده میشود. ViewModel از زمانی که شما برای اولین بار یک ViewModel درخواست میکنید تا زمانی که فعالیت تمام شده و از بین برود، وجود دارد.
پاک کردن وابستگیهای ViewModel
ViewModel متد onCleared را زمانی فراخوانی میکند که ViewModelStoreOwner آن را در طول چرخه حیاتش از بین ببرد. این به شما امکان میدهد هرگونه کار یا وابستگی که از چرخه حیات ViewModel پیروی میکند را پاک کنید.
مثال زیر جایگزینی برای viewModelScope را نشان میدهد. viewModelScope یک CoroutineScope داخلی است که به طور خودکار چرخه حیات ViewModel را دنبال میکند. ViewModel از آن برای راهاندازی عملیات مرتبط با کسب و کار استفاده میکند. اگر میخواهید به جای viewModelScope از یک scope سفارشی برای آزمایش آسانتر استفاده کنید، ViewModel میتواند یک CoroutineScope به عنوان یک وابستگی در سازنده خود دریافت کند. هنگامی که ViewModelStoreOwner در پایان چرخه حیات ViewModel، آن را پاک میکند، ViewModel نیز CoroutineScope لغو میکند.
class MyViewModel(
private val coroutineScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {
// Other ViewModel logic ...
override fun onCleared() {
coroutineScope.cancel()
}
}
از نسخه چرخه حیات ۲.۵ و بالاتر، میتوانید یک یا چند شیء Closeable را به سازنده ViewModel ارسال کنید که هنگام پاک شدن نمونه ViewModel، به طور خودکار بسته میشود.
class CloseableCoroutineScope(
context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
class MyViewModel(
private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
// Other ViewModel logic ...
}
بهترین شیوهها
در ادامه چندین راهکار کلیدی که باید هنگام پیادهسازی ViewModel دنبال کنید، آورده شده است:
- به دلیل محدودهبندی آنها ، از ViewModelها به عنوان جزئیات پیادهسازی یک نگهدارنده وضعیت در سطح صفحه نمایش استفاده کنید. از آنها به عنوان نگهدارنده وضعیت اجزای رابط کاربری قابل استفاده مجدد مانند گروههای تراشه یا فرمها استفاده نکنید. در غیر این صورت، نمونه ViewModel یکسانی را در کاربردهای مختلف یک جزء رابط کاربری تحت ViewModelStoreOwner یکسان دریافت خواهید کرد، مگر اینکه از یک کلید مدل نمای صریح برای هر تراشه استفاده کنید.
- ViewModelها نباید از جزئیات پیادهسازی رابط کاربری (UI) اطلاعی داشته باشند. نام متدهایی که ViewModel API ارائه میدهد و نام فیلدهای وضعیت رابط کاربری را تا حد امکان عمومی نگه دارید. به این ترتیب، ViewModel شما میتواند هر نوع رابط کاربری را در خود جای دهد: تلفن همراه، تبلت تاشو، یا حتی کرومبوک!
- از آنجایی که ViewModelها میتوانند به طور بالقوه عمر طولانیتری نسبت به
ViewModelStoreOwnerداشته باشند، نباید هیچ ارجاعی به APIهای مرتبط با چرخه حیات مانندContextیاResourcesداشته باشند تا از نشت حافظه جلوگیری شود. - ViewModelها را به کلاسها، توابع یا سایر اجزای رابط کاربری ارسال نکنید. از آنجا که پلتفرم آنها را مدیریت میکند، باید آنها را تا حد امکان نزدیک به پلتفرم نگه دارید - نزدیک به Activity، تابع قابل ترکیب در سطح صفحه یا مقصد Navigation. این کار از دسترسی اجزای سطح پایینتر به دادهها و منطق بیشتر از نیازشان جلوگیری میکند.
اطلاعات بیشتر
با پیچیدهتر شدن دادههایتان، ممکن است بخواهید یک کلاس جداگانه فقط برای بارگذاری دادهها داشته باشید. هدف ViewModel ، کپسولهسازی دادهها برای یک کنترلکننده رابط کاربری است تا دادهها بتوانند از تغییرات پیکربندی در امان بمانند. برای کسب اطلاعات در مورد نحوه بارگذاری، حفظ و مدیریت دادهها در طول تغییرات پیکربندی، به بخش «وضعیتهای رابط کاربری ذخیرهشده» مراجعه کنید.
راهنمای معماری برنامه اندروید، ساخت یک کلاس مخزن (repository) برای مدیریت این توابع را پیشنهاد میدهد.
منابع اضافی
برای اطلاعات بیشتر در مورد کلاس ViewModel ، به منابع زیر مراجعه کنید.
مستندات
محتوا را مشاهده میکند
نمونهها
برای شما توصیه میشود
- توجه: متن لینک زمانی نمایش داده میشود که جاوا اسکریپت غیرفعال باشد.
- استفاده از کوروتینهای کاتلین با کامپوننتهای آگاه از چرخه عمر
- ذخیره حالتهای رابط کاربری
- بارگذاری و نمایش دادههای صفحهبندیشده
