Tạo ViewModel có các phần phụ thuộc Một phần của Android Jetpack.

Làm theo các phương pháp hay nhất về thao tác chèn phần phụ thuộc, ViewModel có thể lấy các phần phụ thuộc làm tham số trong hàm dựng. Đây chủ yếu là các loại từ lớp miền hoặc lớp dữ liệu. Vì khung cung cấp ViewModel, nên bạn cần có một cơ chế đặc biệt để tạo các thực thể của khung đó. Cơ chế đó là giao diện ViewModelProvider.Factory. Chỉ các cách triển khai giao diện này mới có thể tạo bản sao các ViewModel trong phạm vi phù hợp.

ViewModel với CreationExtras

Nếu một lớp ViewModel nhận phần phụ thuộc trong hàm khởi tạo của lớp đó, hãy cung cấp một nhà máy triển khai giao diện ViewModelProvider.Factory. Ghi đè hàm create(Class<T>, CreationExtras) để cung cấp bản sao mới của ViewModel.

CreationExtras cho phép bạn truy cập thông tin liên quan giúp tạo thực thể cho ViewModel. Dưới đây là danh sách các khoá có thể truy cập từ lớp bổ sung:

Khoá Chức năng
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY Cấp quyền truy cập vào khoá tuỳ chỉnh mà bạn đã truyền đến ViewModelProvider.get().
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY Cung cấp quyền truy cập vào thực thể của lớp Application.
SavedStateHandleSupport.DEFAULT_ARGS_KEY Cấp quyền truy cập vào Gói đối số mà bạn nên sử dụng để tạo SavedStateHandle.
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY Cấp quyền truy cập vào SavedStateRegistryOwner đang được dùng để tạo ViewModel.
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY Cấp quyền truy cập vào ViewModelStoreOwner đang được dùng để tạo ViewModel.

Để tạo một phiên bản mới của SavedStateHandle, hãy sử dụng hàm CreationExtras.createSavedStateHandle() và truyền hàm đó vào ViewModel.

Sáng tạo bổ sung với APP_KEY

Sau đây là ví dụ về cách cung cấp một thực thể của ViewModel lấy kho lưu trữ thuộc phạm vi lớp ApplicationSavedStateHandle làm phần phụ thuộc:

Kotlin

    import androidx.lifecycle.SavedStateHandle
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.ViewModelProvider
    import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
    import androidx.lifecycle.createSavedStateHandle
    import androidx.lifecycle.viewmodel.CreationExtras

    class MyViewModel(
        private val myRepository: MyRepository,
        private val savedStateHandle: SavedStateHandle
    ) : ViewModel() {

        // ViewModel logic
        // ...

        // Define ViewModel factory in a companion object
        companion object {

            val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
                @Suppress("UNCHECKED_CAST")
                override fun <T : ViewModel> create(
                    modelClass: Class<T>,
                    extras: CreationExtras
                ): T {
                    // Get the Application object from extras
                    val application = checkNotNull(extras[APPLICATION_KEY])
                    // Create a SavedStateHandle for this ViewModel from extras
                    val savedStateHandle = extras.createSavedStateHandle()

                    return MyViewModel(
                        (application as MyApplication).myRepository,
                        savedStateHandle
                    ) as T
                }
            }
        }
    }

Java

import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;

import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.viewmodel.ViewModelInitializer;

public class MyViewModel extends ViewModel {

    public MyViewModel(
        MyRepository myRepository,
        SavedStateHandle savedStateHandle
    ) { /* Init ViewModel here */ }

    static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>(
        MyViewModel.class,
        creationExtras -> {
            MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY);
            assert app != null;
            SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras);

            return new MyViewModel(app.getMyRepository(), savedStateHandle);
        }
    );
}

Sau đó, bạn có thể sử dụng nhà máy này khi truy xuất một phiên bản của ViewModel:

Kotlin

import androidx.activity.viewModels

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels { MyViewModel.Factory }

    // Rest of Activity code
}

Java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class MyActivity extends AppCompatActivity {

MyViewModel myViewModel = new ViewModelProvider(
    this,
    ViewModelProvider.Factory.from(MyViewModel.initializer)
).get(MyViewModel.class);

// Rest of Activity code
}

Jetpack Compose

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory)
) {
    // ...
}

Ngoài ra, hãy sử dụng DSL nhà máy của ViewModel để tạo các nhà máy bằng cách sử dụng API Kotlin đúng cách hơn:

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory

class MyViewModel(
    private val myRepository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    // ViewModel logic

    // Define ViewModel factory in a companion object
    companion object {
        val Factory: ViewModelProvider.Factory = viewModelFactory {
            initializer {
                val savedStateHandle = createSavedStateHandle()
                val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository
                MyViewModel(
                    myRepository = myRepository,
                    savedStateHandle = savedStateHandle
                )
            }
        }
    }
}

Truyền tham số tuỳ chỉnh dưới dạng CreationExtras

Bạn có thể truyền các phần phụ thuộc đến ViewModel thông qua CreationExtras bằng cách tạo khoá tuỳ chỉnh. Điều này có thể hữu ích nếu ViewModel phụ thuộc vào các đối tượng không thể truy cập thông qua lớp ApplicationAPPLICATION_KEY. Ví dụ về điều này là khi ViewModel được tạo bên trong mô-đun Kotlin Đa nền tảng và do đó không có quyền truy cập vào các phần phụ thuộc Android.

Trong ví dụ này, ViewModel xác định một khoá tuỳ chỉnh và sử dụng khoá đó trong ViewModelProvider.Factory.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory

class MyViewModel(
    private val myRepository: MyRepository,
) : ViewModel() {
    // ViewModel logic

    // Define ViewModel factory in a companion object
    companion object {

        // Define a custom key for your dependency
        val MY_REPOSITORY_KEY = object : CreationExtras.Key<MyRepository> {}

        val Factory: ViewModelProvider.Factory = viewModelFactory {
            initializer {
                // Get the dependency in your factory
                val myRepository = this[MY_REPOSITORY_KEY] as MyRepository
                MyViewModel(
                    myRepository = myRepository,
                )
            }
        }
    }
}

Bạn có thể tạo bản sao ViewModel bằng CreationExtras.Key từ một ViewModelStoreOwner như ComponentActivity, Fragment hoặc NavBackStackEntry hoặc bằng Jetpack Compose.

Kotlin

import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.MutableCreationExtras
// ...
    // Use from ComponentActivity, Fragment, NavBackStackEntry,
    // or another ViewModelStoreOwner.
    val viewModelStoreOwner: ViewModelStoreOwner = this
    val myViewModel: MyViewModel = ViewModelProvider.create(
        viewModelStoreOwner,
        factory = MyViewModel.Factory,
        extras = MutableCreationExtras().apply {
            set(MyViewModel.MY_REPOSITORY_KEY, myRepository)
        },
    )[MyViewModel::class]

Jetpack Compose

import androidx.lifecycle.viewmodel.MutableCreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
// ...
@Composable
fun MyApp(myRepository: MyRepository) {
    val extras = MutableCreationExtras().apply {
        set(MyViewModel.MY_REPOSITORY_KEY, myRepository)
    }
    val viewModel: MyViewModel = viewModel(
        factory = MyViewModel.Factory,
        extras = extras,
    )
}

Nhà máy (factory) dành cho phiên bản ViewModel trước phiên bản 2.5.0

Nếu đang sử dụng phiên bản ViewModel cũ hơn phiên bản 2.5.0, bạn cần cung cấp các nhà máy (trạng thái ban đầu) từ một tập hợp con các lớp mở rộng ViewModelProvider.Factory và triển khai hàm create(Class<T>). Tuỳ thuộc vào phần phụ thuộc mà ViewModel cần, bạn cần mở rộng một lớp khác từ:

Nếu không cần Application hoặc SavedStateHandle, bạn chỉ cần mở rộng từ ViewModelProvider.Factory.

Ví dụ sau đây sử dụng một AbstractSavedStateViewModelFactory cho ViewModel có kho lưu trữ và loại SavedStateHandle làm phần phụ thuộc:

Kotlin

class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {

// ViewModel logic ...

// Define ViewModel factory in a companion object
companion object {
    fun provideFactory(
        myRepository: MyRepository,
        owner: SavedStateRegistryOwner,
        defaultArgs: Bundle? = null,
    ): AbstractSavedStateViewModelFactory =
        object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(
                key: String,
                modelClass: Class<T>,
                handle: SavedStateHandle
            ): T {
                return MyViewModel(myRepository, handle) as T
            }
        }
    }
}

Java

import androidx.annotation.NonNull;
import androidx.lifecycle.AbstractSavedStateViewModelFactory;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    public MyViewModel(
        MyRepository myRepository,
        SavedStateHandle savedStateHandle
    ) { /* Init ViewModel here */ }
}

public class MyViewModelFactory extends AbstractSavedStateViewModelFactory {

    private final MyRepository myRepository;

    public MyViewModelFactory(
        MyRepository myRepository
    ) {
        this.myRepository = myRepository;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    protected <T extends ViewModel> T create(
        @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle
    ) {
        return (T) new MyViewModel(myRepository, handle);
    }
}

Sau đó, bạn có thể sử dụng nhà máy (trạng thái ban đầu) để truy xuất ViewModel:

Kotlin

import androidx.activity.viewModels

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels {
        MyViewModel.provideFactory((application as MyApplication).myRepository, this)
    }

    // Rest of Activity code
}

Java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class MyActivity extends AppCompatActivity {

    MyViewModel myViewModel = new ViewModelProvider(
        this,
        ViewModelProvider.Factory.from(MyViewModel.initializer)
    ).get(MyViewModel.class);

    // Rest of Activity code
}

Jetpack Compose

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    viewModel: MyViewModel = viewModel(
        factory = MyViewModel.provideFactory(
            (LocalContext.current.applicationContext as MyApplication).myRepository,
            owner = LocalSavedStateRegistryOwner.current
        )
    )
) {
    // ...
}