API การกำหนดขอบเขต ViewModel   ส่วนหนึ่งของ Android Jetpack

ขอบเขตเป็นกุญแจสำคัญในการใช้ ViewModel อย่างมีประสิทธิภาพ ViewModel แต่ละรายการจะกำหนดขอบเขตเป็นออบเจ็กต์ที่ใช้ViewModelStoreOwnerอินเทอร์เฟซ มี API หลายรายการที่ช่วยให้คุณจัดการขอบเขตของ ViewModel ได้ง่ายขึ้น เอกสารนี้จะอธิบายเทคนิคสำคัญบางอย่างที่คุณควรทราบ

ViewModelProvider.get() วิธีนี้ช่วยให้คุณได้รับอินสแตนซ์ของ ViewModel ที่กำหนดขอบเขตไว้กับ ViewModelStoreOwner สำหรับผู้ใช้ Kotlin จะมี ฟังก์ชันส่วนขยายที่แตกต่างกันสำหรับกรณีการใช้งานที่พบบ่อยที่สุด การติดตั้งใช้งานฟังก์ชันส่วนขยาย Kotlin ทั้งหมดใช้ ViewModelProvider API เป็นส่วนประกอบพื้นฐานในการทำงาน

ViewModel ที่กำหนดขอบเขตไว้ที่ ViewModelStoreOwner ที่ใกล้ที่สุด

คุณสามารถกำหนดขอบเขต ViewModel ให้กับ Composable, กิจกรรม หรือ ปลายทางของกราฟการนำทางได้ ฟังก์ชัน viewModel() ใน Compose ช่วยให้คุณรับอินสแตนซ์ของ ViewModel ที่กำหนดขอบเขตไว้กับ ViewModelStoreOwner ที่ใกล้ที่สุดได้

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the closest ViewModelStoreOwner provided
    // via the LocalViewModelStoreOwner CompositionLocal. In order of proximity,
    // this could be the destination of a Navigation graph
    // or the host Activity.
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

ViewModel ที่กำหนดขอบเขตไว้สำหรับ ViewModelStoreOwner

ฟังก์ชัน viewModel() จะใช้พารามิเตอร์ viewModelStoreOwner ที่ไม่บังคับ ซึ่งคุณใช้เพื่อระบุ ViewModelStoreOwner ที่อินสแตนซ์ของ ViewModel มีขอบเขตได้

import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.ViewModelStoreOwner

@Composable
fun MyScreen(
    // A custom owner passed in, such as a parent NavBackStackEntry
    customOwner: ViewModelStoreOwner,
    // The ViewModel is now scoped to the provided customOwner
    viewModel: MyViewModel = viewModel(viewModelStoreOwner = customOwner)
) {
    /* ... */
}

ViewModel ที่กำหนดขอบเขตไว้กับ Composable

คุณสามารถใช้ rememberViewModelStoreOwner() เพื่อกำหนดขอบเขต ViewModel โดยตรงกับ ตำแหน่งที่เรียกใช้ของ Composable วิธีนี้มีประโยชน์อย่างยิ่งสำหรับคอมโพเนนต์ UI ที่ เพิ่มหรือนำออกจากหน้าจอแบบไดนามิกตามสถานะ เช่น รายการของหน้าเว็บหรือรายการที่โหลดแบบ Lazy Loading เมื่อ Composable ที่เป็นเจ้าของ ViewModelStoreOwner ออกจาก Composition ระบบจะล้าง ViewModelStore ที่เชื่อมโยงและทำลาย ViewModel

ใช้ rememberViewModelStoreOwner() เพื่อสร้างที่เก็บข้อมูลที่รับรู้ถึงวงจร ซึ่งจะยังคงอยู่แม้จะมีการเปลี่ยนแปลงการกำหนดค่า

@Composable
fun RememberViewModelStoreOwnerSample() {
    // Create a ViewModelStoreOwner scoped to this specific call site.
    // When this composable leaves the composition,
    // the associated ViewModelStore will be cleared.
    val scopedOwner = rememberViewModelStoreOwner()

    CompositionLocalProvider(LocalViewModelStoreOwner provides scopedOwner) {
        // This ViewModel is scoped to `scopedOwner`.
        // It will survive configuration changes but will be cleared when
        // the composable is removed from the UI tree.
        val viewModel = viewModel { TestViewModel("scoped_data") }
        // Use the ViewModel
    }
}

สำหรับการติดตั้งใช้งานที่ซับซ้อนมากขึ้น เช่น HorizontalPager หรือกรณีที่ต้องใช้ขอบเขตอิสระหลายรายการ ให้ใช้ rememberViewModelStoreProvider() ซึ่งจะช่วยให้คุณสร้างViewModelStoreOwnerอินสแตนซ์ที่แตกต่างกันสำหรับคีย์ต่างๆ (เช่น ดัชนีหน้าเว็บ) ได้ วิธีนี้จะช่วยให้แต่ละหน้าคงสถานะ ViewModel ของตัวเองไว้ได้

@Composable
fun RememberViewModelStoreProviderSample() {
    val storeProvider = rememberViewModelStoreProvider()
    val pages = listOf("Page 1", "Page 2", "Page 3")

    HorizontalPager(pageCount = pages.size) { page ->
        // Create a ViewModelStoreOwner for the specific page using the provider.
        val pageOwner = rememberViewModelStoreOwner(provider = storeProvider, key = page)

        CompositionLocalProvider(LocalViewModelStoreOwner provides pageOwner) {
            val pageViewModel = viewModel { TestViewModel(pages[page]) }
            // Use pageViewModel
        }
    }
}

ViewModel ที่กำหนดขอบเขตไว้ในกราฟการนำทาง

กราฟการนำทางยังเป็นเจ้าของที่เก็บ ViewModel ด้วย หากใช้ Navigation Compose คุณจะรับอินสแตนซ์ของ ViewModel ที่กำหนดขอบเขตไว้กับกราฟการนำทางได้ด้วยฟังก์ชัน getBackStackEntry()

viewModel() จะดึงข้อมูลอินสแตนซ์จาก ViewModelStoreOwner ที่ใกล้ที่สุด ซึ่งให้บริการโดย LocalViewModelStoreOwner CompositionLocal ในแอปพลิเคชัน Compose ทั่วไปที่ใช้ Jetpack Navigation เจ้าของนี้คือรายการใน Back Stack ของการนำทางปัจจุบัน ซึ่งหมายความว่า ViewModel จะยังคงอยู่ในหน่วยความจำตราบใดที่ปลายทางนั้นยังอยู่ใน Back Stack

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        // Retrieve the NavBackStackEntry of "parentNavigationRoute"
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }
        // Get the ViewModel scoped to the `parentNavigationRoute` Nav graph
        val parentViewModel: SharedViewModel = viewModel(parentEntry)
        // ...
    }
}

หากใช้ Hilt นอกเหนือจาก Jetpack Navigation คุณจะใช้ API hiltNavGraphViewModels(graphId) ได้ดังนี้

import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }

        // ViewModel API available in hilt.hilt-navigation-compose
        // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph
        // and is provided using the Hilt-generated ViewModel factory
        val parentViewModel: SharedViewModel = hiltViewModel(parentEntry)
        // ...
    }
}

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับ ViewModel และขอบเขตได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

เอกสารประกอบ

ดูเนื้อหา