Tworzenie modeli widoków z zależnościami Część stanowiąca część Androida Jetpack.

Zgodnie ze sprawdzonymi metodami wstrzykiwania zależności obiekty typu ViewModel mogą przyjmować zależności jako parametry w swoim konstruktorze. Są to głównie typy z warstwy domeny lub danych. Platforma udostępnia obiekty ViewModel, dlatego do tworzenia ich instancji wymagany jest specjalny mechanizm. Tym mechanizmem jest interfejs ViewModelProvider.Factory. Tylko implementacje tego interfejsu mogą tworzyć instancje ViewModel we właściwym zakresie.

Modele wyświetlania z dodatkami do tworzenia

Jeśli klasa ViewModel otrzymuje zależności w swoim konstruktorze, podaj fabrykę implementującą interfejs ViewModelProvider.Factory. Zastąp funkcję create(Class<T>, CreationExtras), aby udostępnić nową instancję ViewModel.

CreationExtras zapewnia dostęp do odpowiednich informacji, które pomagają zaimplementować model ViewModel. Oto lista kluczy, do których można uzyskać dostęp za pomocą dodatków:

Klucz Funkcjonalność
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY Zapewnia dostęp do klucza niestandardowego przekazanego do ViewModelProvider.get().
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY Daje dostęp do instancji klasy Application.
SavedStateHandleSupport.DEFAULT_ARGS_KEY Daje dostęp do pakietu argumentów, którego należy użyć do utworzenia funkcji SavedStateHandle.
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY Daje dostęp do obiektu SavedStateRegistryOwner używanego do tworzenia obiektu ViewModel.
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY Daje dostęp do obiektu ViewModelStoreOwner używanego do tworzenia obiektu ViewModel.

Aby utworzyć nową instancję obiektu SavedStateHandle, użyj funkcji CreationExtras.createSavedStateHandle().createSavedStateHandle()) i przekaż ją do ViewModel.

Poniżej znajdziesz przykład, jak udostępnić instancję ViewModel, która pobiera repozytorium ograniczone do klasy Application i SavedStateHandle jako zależności:

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

Następnie możesz użyć tej fabryki podczas pobierania instancji obiektu 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)
) {
    // ...
}

Możesz też użyć DSL fabryki ViewModel do utworzenia fabryk za pomocą bardziej idiomatycznego interfejsu API 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.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
                )
            }
        }
    }
}

Fabryki ViewModel w wersji starszej niż 2.5.0

Jeśli używasz wersji ViewModel starszej niż 2.5.0, musisz podać fabryki z podzbioru klas, które rozszerzają ViewModelProvider.Factory i implementują funkcję create(Class<T>). W zależności od zależności, których wymaga ViewModel, inną klasę trzeba rozszerzyć z poziomu:

Jeśli nie potrzebujesz właściwości Application lub SavedStateHandle, po prostu przedłuż ją z poziomu ViewModelProvider.Factory.

W tym przykładzie użyto AbstractSavedStateViewModelFactory dla obiektu ViewModel, który jako zależność pobiera repozytorium i typ SavedStateHandle:

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

Następnie możesz użyć ustawień fabrycznych, aby pobrać swój model 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
        )
    )
) {
    // ...
}