종속 항목이 있는 ViewModel 만들기  Android Jetpack의 구성요소

종속 항목 삽입의 권장사항에 따라 ViewModel은 종속 항목을 생성자의 매개변수로 사용할 수 있습니다. 이는 대부분 도메인 또는 데이터 레이어의 유형입니다. 프레임워크에서 ViewModel을 제공하기 때문에 ViewModel의 인스턴스를 만들려면 특수한 메커니즘이 필요합니다. 이 메커니즘이 ViewModelProvider.Factory 인터페이스입니다. 이 인터페이스의 구현만 올바른 범위에서 ViewModel을 인스턴스화할 수 있습니다.

CreationExtras가 포함된 ViewModel

ViewModel 클래스가 생성자의 종속 항목을 수신하면 ViewModelProvider.Factory 인터페이스를 구현하는 팩토리를 제공합니다. ViewModel의 새 인스턴스를 제공하도록 create(Class, CreationExtras) 함수를 재정의합니다.

CreationExtras를 사용하면 ViewModel을 인스턴스화하는 데 유용한 관련 정보에 액세스할 수 있습니다. Extras에서 액세스할 수 있는 키 목록은 다음과 같습니다.

기능
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY ViewModelProvider.get()에 전달한 맞춤 키에 대한 액세스 권한을 제공합니다.
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY Application 클래스의 인스턴스에 대한 액세스 권한을 제공합니다.
SavedStateHandleSupport.DEFAULT_ARGS_KEY SavedStateHandle을 구성하는 데 사용해야 하는 인수의 Bundle에 대한 액세스 권한을 제공합니다.
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY ViewModel을 구성하는 데 사용되는 SavedStateRegistryOwner에 대한 액세스 권한을 제공합니다.
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY ViewModel을 구성하는 데 사용되는 ViewModelStoreOwner에 대한 액세스 권한을 제공합니다.

SavedStateHandle의 새 인스턴스를 만들려면 CreationExtras.createSavedStateHandle() 함수를 사용하여 ViewModel에 전달합니다.

APPLICATION_KEY가 있는 CreationExtras

다음은 Application 클래스 및 SavedStateHandle로 범위가 지정된 저장소를 종속 항목으로 사용하는 ViewModel의 인스턴스를 제공하는 방법을 보여 주는 예입니다.

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

자바

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

그런 다음 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)
) {
    // ...
}

또는 ViewModel 팩토리 DSL을 사용하여 더 자연스러운 Kotlin API로 팩토리를 만듭니다.

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

맞춤 매개변수를 CreationExtras로 전달

커스텀 키를 만들어 CreationExtras를 통해 ViewModel에 종속 항목을 전달할 수 있습니다. 이 기능은 ViewModelApplication 클래스 및 APPLICATION_KEY를 통해 액세스할 수 없는 객체에 의존하는 경우 유용할 수 있습니다. 예를 들어 ViewModel가 Kotlin 멀티플랫폼 모듈 내에서 생성되어 Android 종속 항목에 액세스할 수 없는 경우를 들 수 있습니다.

이 예에서 ViewModel는 맞춤 키를 정의하고 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 {}

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

ComponentActivity, Fragment, NavBackStackEntry와 같은 ViewModelStoreOwner에서 CreationExtras.Key를 사용하여 ViewModel를 인스턴스화하거나 Jetpack Compose를 사용하여 ViewModel를 인스턴스화할 수 있습니다.

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

2.5.0 이전의 ViewModel 버전에 대한 팩토리

2.5.0 이전 버전의 ViewModel를 사용하는 경우 ViewModelProvider.Factory를 확장하는 클래스의 하위 집합에서 팩토리를 제공하고 create(Class) 함수를 구현해야 합니다. ViewModel에 필요한 종속 항목에 따라 다음에서 다른 클래스를 확장해야 합니다.

Application 또는 SavedStateHandle이 필요하지 않으면 ViewModelProvider.Factory에서 확장합니다.

다음 예에서는 저장소 및 SavedStateHandle 유형을 종속 항목으로 사용하는 ViewModel에 대해 AbstractSavedStateViewModelFactory를 사용합니다.

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,
                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 modelClass, @NonNull SavedStateHandle handle
    ) {
        return (T) new MyViewModel(myRepository, handle);
    }
}

그런 다음 팩토리를 사용하여 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
        )
    )
) {
    // ...
}