Tạo ViewModel có các phần phụ thuộc (Khung hiển thị)   Một phần của Android Jetpack.

Các khái niệm và cách triển khai Jetpack Compose

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 các 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 một phiên bản mới của ViewModel.

CreationExtras có APPLICATION_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:

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
}

Truyền thông 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 một khoá tuỳ chỉnh. Điều này có thể hữu ích nếu ViewModel của bạn 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ề trường hợp này là khi ViewModel của bạn được tạo bên trong một mô-đun Kotlin Multiplatform 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 thực thể ViewModel bằng CreationExtras.Key từ ViewModelStoreOwner, chẳng hạn như ComponentActivity, Fragment hoặc NavBackStackEntry.

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]

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 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
}