نمای کلی ViewModel بخشی از Android Jetpack .

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

برای اطلاعات بیشتر در مورد دارندگان ایالت، به راهنمای دارندگان ایالت مراجعه کنید. به طور مشابه، برای اطلاعات بیشتر در مورد لایه UI به طور کلی، راهنمای لایه UI را ببینید.

مزایای ViewModel

جایگزین ViewModel یک کلاس ساده است که داده هایی را که در UI خود نمایش می دهید نگه می دارد. این می تواند هنگام پیمایش بین فعالیت ها یا مقصدهای ناوبری مشکل ساز شود. انجام این کار باعث از بین رفتن داده ها می شود اگر آنها را با استفاده از مکانیسم حالت ذخیره سازی ذخیره نکنید. ViewModel یک API مناسب برای ماندگاری داده ارائه می دهد که این مشکل را حل می کند.

مزایای کلیدی کلاس ViewModel اساساً دو است:

  • این به شما امکان می دهد وضعیت رابط کاربری را حفظ کنید.
  • دسترسی به منطق تجاری را فراهم می کند.

ماندگاری

ViewModel هم از طریق حالتی که ViewModel دارد و هم از طریق عملیاتی که ViewModel راه‌اندازی می‌کند، تداوم می‌دهد. این ذخیره سازی به این معنی است که شما مجبور نیستید از طریق تغییرات رایج در پیکربندی، مانند چرخش صفحه، دوباره داده ها را واکشی کنید.

دامنه

هنگامی که یک ViewModel را نمونه‌سازی می‌کنید، به آن یک شی منتقل می‌کنید که رابط ViewModelStoreOwner را پیاده‌سازی می‌کند. این ممکن است یک مقصد ناوبری، نمودار ناوبری، فعالیت، قطعه یا هر نوع دیگری باشد که رابط را پیاده‌سازی می‌کند. سپس ViewModel شما به چرخه حیات ViewModelStoreOwner اختصاص می یابد. تا زمانی که ViewModelStoreOwner آن برای همیشه از بین برود در حافظه باقی می ماند.

طیفی از کلاس‌ها زیر کلاس‌های مستقیم یا غیرمستقیم رابط ViewModelStoreOwner هستند. زیر کلاس های مستقیم ComponentActivity ، Fragment و NavBackStackEntry هستند. برای فهرست کامل زیر کلاس‌های غیرمستقیم، به مرجع ViewModelStoreOwner مراجعه کنید.

هنگامی که قطعه یا فعالیتی که ViewModel به آن اختصاص داده شده است از بین می رود، کار ناهمزمان در ViewModel که محدوده آن را در نظر گرفته است ادامه می یابد. این کلید پایداری است.

برای اطلاعات بیشتر، بخش زیر را در مورد چرخه عمر ViewModel ببینید.

SavedStateHandle

SavedStateHandle به شما اجازه می دهد تا داده ها را نه تنها از طریق تغییرات پیکربندی، بلکه در سراسر فرآیند بازیابی کنید. به این معنی که شما را قادر می سازد تا وضعیت رابط کاربری را دست نخورده نگه دارید حتی زمانی که کاربر برنامه را می بندد و بعداً آن را باز می کند.

دسترسی به منطق کسب و کار

حتی اگر اکثریت قریب به اتفاق منطق کسب و کار در لایه داده وجود دارد، لایه UI نیز می تواند حاوی منطق تجاری باشد. این می تواند در هنگام ترکیب داده ها از چندین مخزن برای ایجاد حالت رابط کاربری صفحه، یا زمانی که نوع خاصی از داده به لایه داده نیاز ندارد، صدق کند.

ViewModel مکان مناسبی برای مدیریت منطق تجاری در لایه UI است. ViewModel همچنین مسئول رسیدگی به رویدادها و تفویض آنها به سایر لایه‌های سلسله مراتبی است، زمانی که منطق تجاری برای اصلاح داده‌های برنامه اعمال شود.

Jetpack Compose

هنگام استفاده از Jetpack Compose، ViewModel ابزار اصلی برای نمایش وضعیت رابط کاربری صفحه برای اجزای سازنده شما است. در یک برنامه ترکیبی، فعالیت ها و قطعات به سادگی میزبان توابع قابل ترکیب شما هستند. این یک تغییر از رویکردهای گذشته است، جایی که ایجاد قطعات قابل استفاده مجدد از UI با فعالیت ها و قطعات آنقدر ساده و شهودی نبود، که باعث می شد آنها به عنوان کنترل کننده های UI بسیار فعال تر باشند.

مهمترین چیزی که هنگام استفاده از ViewModel با Compose باید در نظر داشته باشید این است که نمی توانید یک ViewModel را به یک Composable محدود کنید. این به این دلیل است که یک Composable یک ViewModelStoreOwner نیست. دو نمونه از یک composable در Composition، یا دو composable مختلف که به یک نوع ViewModel دسترسی دارند تحت ViewModelStoreOwner یکسان، نمونه مشابهی از ViewModel را دریافت می‌کنند، که اغلب رفتار مورد انتظار نیست.

برای بهره مندی از مزایای ViewModel در Compose، هر صفحه را در یک Fragment یا Activity میزبانی کنید، یا از Compose Navigation استفاده کنید و از ViewModels در توابع ترکیبی تا حد امکان نزدیک به مقصد Navigation استفاده کنید. دلیل آن این است که می‌توانید یک ViewModel را به مقصدهای ناوبری، نمودارهای ناوبری، فعالیت‌ها و بخش‌ها محدود کنید.

برای اطلاعات بیشتر، به راهنمای بالا بردن حالت برای Jetpack Compose مراجعه کنید.

یک 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,
            )
        }
    }
}

جاوا

public class DiceUiState {
    private final Integer firstDieValue;
    private final Integer secondDieValue;
    private final int numberOfRolls;

    // ...
}

public class DiceRollViewModel extends ViewModel {

    private final MutableLiveData<DiceUiState> uiState =
        new MutableLiveData(new DiceUiState(null, null, 0));
    public LiveData<DiceUiState> getUiState() {
        return uiState;
    }

    public void rollDice() {
        Random random = new Random();
        uiState.setValue(
            new DiceUiState(
                random.nextInt(7) + 1,
                random.nextInt(7) + 1,
                uiState.getValue().getNumberOfRolls() + 1
            )
        );
    }
}

سپس می توانید از طریق یک اکتیویتی به ViewModel به صورت زیر دسترسی داشته باشید:

کاتلین

import androidx.activity.viewModels

class DiceRollActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same DiceRollViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: DiceRollViewModel by viewModels()
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

جاوا

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
        model.getUiState().observe(this, uiState -> {
            // update UI
        });
    }
}

Jetpack Compose

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 شامل پشتیبانی از کوروتین های Kotlin است. این می تواند کار ناهمزمان را به همان شیوه ای که در حالت UI ادامه می دهد، ادامه دهد.

برای اطلاعات بیشتر، استفاده از کوروتین های Kotlin با اجزای معماری Android را ببینید.

چرخه عمر ViewModel

چرخه عمر ViewModel مستقیماً با محدوده آن مرتبط است. یک ViewModel در حافظه باقی می ماند تا زمانی که ViewModelStoreOwner که محدوده آن به آن اختصاص داده شده است ناپدید شود. این ممکن است در زمینه های زیر رخ دهد:

  • در مورد یک فعالیت، زمانی که به پایان می رسد.
  • در مورد قطعه، وقتی جدا می شود.
  • در مورد ورودی ناوبری، زمانی که از پشته حذف شده است.

این باعث می شود ViewModels یک راه حل عالی برای ذخیره داده هایی باشد که از تغییرات پیکربندی جان سالم به در می برند.

شکل 1 حالت های مختلف چرخه حیات یک فعالیت را در حین چرخش و سپس اتمام آن نشان می دهد. این تصویر همچنین طول عمر ViewModel را در کنار چرخه حیات فعالیت مرتبط نشان می دهد. این نمودار خاص حالت های یک فعالیت را نشان می دهد. همان حالات اساسی برای چرخه حیات یک قطعه اعمال می شود.

چرخه عمر ViewModel را با تغییر وضعیت یک فعالیت نشان می دهد.

شما معمولاً اولین باری که سیستم متد onCreate() یک شی اکتیویتی را فراخوانی می کند، یک ViewModel درخواست می کنید. سیستم ممکن است در طول یک فعالیت، چندین بار onCreate() فراخوانی کند، مانند زمانی که صفحه دستگاه می چرخد. ViewModel از زمانی که برای اولین بار یک ViewModel درخواست می کنید تا زمانی که فعالیت تمام شود و از بین برود وجود دارد.

پاک کردن وابستگی های ViewModel

ViewModel زمانی متد onCleared را فراخوانی می کند که ViewModelStoreOwner آن را در طول چرخه عمر خود از بین می برد. این به شما امکان می‌دهد تا هر کار یا وابستگی‌هایی را که از چرخه عمر ViewModel پیروی می‌کنند پاک کنید.

مثال زیر جایگزینی برای viewModelScope را نشان می دهد. viewModelScope یک CoroutineScope داخلی است که به طور خودکار چرخه حیات ViewModel را دنبال می کند. ViewModel از آن برای راه اندازی عملیات مرتبط با کسب و کار استفاده می کند. اگر می‌خواهید از یک محدوده سفارشی به جای viewModelScope برای آزمایش آسان‌تر استفاده کنید، 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()
    }
}

از چرخه حیات نسخه 2.5 و بالاتر، می توانید یک یا چند شی 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 دنبال کنید:

  • به دلیل محدوده آنها ، از ViewModels به عنوان جزئیات پیاده سازی یک نگهدارنده حالت سطح صفحه استفاده کنید. از آنها به عنوان دارندگان حالت اجزای رابط کاربری قابل استفاده مجدد مانند گروه های تراشه یا فرم ها استفاده نکنید. در غیر این صورت، نمونه ViewModel یکسان را در استفاده‌های مختلف از یک مؤلفه UI در همان ViewModelStoreOwner دریافت خواهید کرد، مگر اینکه از یک کلید مدل view صریح در هر تراشه استفاده کنید.
  • ViewModels نباید از جزئیات پیاده سازی UI اطلاع داشته باشد. نام روش‌هایی را که ViewModel API نشان می‌دهد و نام‌های فیلدهای حالت UI را تا حد امکان عمومی نگه دارید. به این ترتیب، ViewModel شما می‌تواند هر نوع رابط کاربری را در خود جای دهد: تلفن همراه، تاشو، تبلت یا حتی Chromebook!
  • از آنجایی که به طور بالقوه می‌توانند بیشتر از ViewModelStoreOwner عمر کنند، ViewModels نباید هیچ مرجعی از APIهای مرتبط با چرخه حیات مانند Context یا Resources داشته باشد تا از نشت حافظه جلوگیری کند.
  • ViewModels را به کلاس‌ها، توابع یا سایر اجزای رابط کاربری منتقل نکنید. از آنجایی که پلتفرم آنها را مدیریت می کند، باید آنها را تا جایی که می توانید نزدیک به آن نگه دارید. نزدیک به عملکرد، قطعه، یا عملکرد قابل ترکیب در سطح صفحه نمایش شما. این مانع از دسترسی اجزای سطح پایین تر به داده ها و منطق بیشتر از نیازشان می شود.

اطلاعات بیشتر

همانطور که داده های شما پیچیده تر می شوند، ممکن است انتخاب کنید که یک کلاس جداگانه برای بارگذاری داده ها داشته باشید. هدف ViewModel این است که داده ها را برای یک کنترلر UI محصور کند تا داده ها از تغییرات پیکربندی جان سالم به در ببرند. برای اطلاعات در مورد نحوه بارگیری، ماندگاری و مدیریت داده ها در تغییرات پیکربندی، به وضعیت های ذخیره شده UI مراجعه کنید.

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

منابع اضافی

برای اطلاعات بیشتر در مورد کلاس ViewModel ، به منابع زیر مراجعه کنید.

مستندات

نمونه ها

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}