본문 바로가기

안드로이드

Hilt를 이용한 ViewModel 객체 생성

Hilt를 사용해서 VIewModel 객체를 뷰(Activity)에서 생성한뒤 사용하는 방법에 관해서 알아보겠습니다.
글의 뒷부분에서 Hilt를 사용하지 않고 viewModel 객체를 생성해서 사용하는 방법도 서술하겠습니다.
우서 이 글의 주제가 Hilt를 이용한 ViewModel 객체의 생성이니 관련 내용을 서술하도록 하겠습니다.
ps. 현재 혼자서 공부하고 있는 프로젝트에서 코드를 사용하고 있어서 주제와 조금 관련이 없는 코드들도 섞여 있을 수 있겠네요.
 
Hilt를 이용한 ViewModel 객체 생성하는 방법
우선 Hilt를 사용하기 위해서는 Application에서 @HiltAndroidApp 어노테이션을 사용해줘야 합니다.
-Hilt를 사용하는 모든 앱은 @HiltAndroidApp으로 주석이 지정된 Application 클래스를 포함해야 합니다.
-애플리케이션 수준 종속 항목 컨테이너 역할을 하는 애플리케이션의 기본 클래스를 비롯하여 Hilt의 코드 생성을 트리거합니다.

 

import android.app.Application
import com.google.firebase.FirebaseApp
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class FirestoreStorageCleanArchitectureApp : Application() {
    override fun onCreate() {
        super.onCreate()
        FirebaseApp.initializeApp(this)
    }
}​

 

그리고 매니페스트에 application을 정의해주세요.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:name=".FirestoreStorageCleanArchitectureApp"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.FireStoreStorageCleanArchitecturePractice"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

 

그리고 뷰단인 Activity에 @AndroidEntryPoint 어노테이션을 추가해주세요.
(만약 본인이 Fragment에서 사용한다고 쳐도 마찬가지 입니다. 어노테이션을 추가해주세요.)

 

import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import com.bumptech.glide.Glide
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    lateinit var imageView : ImageView
    private val startForResult =
        registerForActivityResult(ActivityResultContracts.GetContent()) {
            it?.let { Glide.with(this).load(it).into(imageView) }
            it?.let { uri -> uploadtImage(uri) }
        }
    private val viewModel: MainActivityViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Logger.addLogAdapter(AndroidLogAdapter())
        imageView = findViewById(R.id.image)
        imageView.setOnClickListener{
            startForResult.launch("image/*")
        }
    }
    fun uploadtImage(uri : Uri){
        viewModel.uploadImage(uri)
    }
}

 

Hilt라이브러리가 Application컴포넌트(객체)가 사용하는게 가능해지면 Hilt는 @AndroidEntryPoint 어노테이션을 가진 클래스에게 의존성 주입을 해주는게 가능합니다.
그러니까 위의 과정에서 Application 클래스에 @HiltAndroidApp 어노테이션을 추가했어야 했던 것이죠.
다음은 ViewModel입니다.
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class MainActivityViewModel @Inject constructor(
    private val useCases: UseCases
) : ViewModel() {
    fun uploadImage(uri: Uri) = viewModelScope.launch {
        useCases.uploadImage(uri)
    }
}
@HiltViewModel 어노테이션을 이용해서 viewModel 객체를 생성합니다.
만약 hilt를 사용하지 않고 viewModel 객체를 생성해야 한다면 아래와 같은 과정을 거치게 됩니다.

파라미터가 없는 경우 viewModel 객체를 생성하는 방법

방법1
ViewModelProvider() 메소드를 사용
파라미터가 없는 경우 viewModel 객체 생성

 

class MainActivityViewModel : ViewModel() {
    ...
}

 

이렇게 하면 view 단에서는 ViewModelProvider 객체를 사용해서 객체를 생성할 수 있습니다.
class MainActivity : Activity() {
    
val mainActivityViewModel =MainActivityViewModel(this).get(MainActivityViewModel::class.java)
}
방법2
by viewModels() 메소드 사용
by viewModels() 메소드를 사용하면 ViewModel을 지연 생성할 수 있습니다.
이 방법을 사용하기 위해서는 gradle에 라이브러리를 추가해야 합니다.
app단계의 build.gradle 에 자신의 상황에 맞춰서 아래와 같이 라이브러리를 추가해줍니다.
(라이브러리의 버전은 본인의 상황에 맞게 확인해주세요.)
dependencies {
    // by viewModels 를 사용하기 위해 추가한 라이브러리
    implementation("androidx.activity:activity-ktx:1.5.0")
    // by viewModels 를 프레그먼트에서 사용하기 위해 추가함
    implementation("androidx.fragment:fragment-ktx:1.6.1")
}
class MyPageFragment : Fragment() {
    private val myPageFragmentViewModel: MyPageFragmentViewModel by viewModels()

    ...
    //onCreate 등에서 뷰모델 객체를 사용하는게 가능합니다.
}

 

파라미터가 있는 경우 viewModel 객체를 생성하는 방법

방법1
ViewModel + Factory (hilt를 사용하지 않는 경우입니다.)
ViewModel은 생성자가 없습니다. 따라서 파라미터가 있는 객체를 생성하는 경우, ViewModelProvider.Factory 를 정의해줘야 합니다.
import androidx.lifecycle.ViewModel 
import androidx.lifecycle.ViewModelProvider

class MainActivityViewModel(string: String) : ViewModel() {
    val result = string
}

class MainActivityViewModelFactory(private val string: String): ViewModelProvider.Factory{
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if(modelClass.isAssignableFrom(MainActivityViewModel::class.java)){
            return MainActivityViewModel(string) as T
        }
        throw IllegalArgumentException("Unknown ViewModel Class")
    }
}
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val viewModelFactory = MainActivityViewModelFactory("test")
        val viewModel = ViewModelProvider(this, viewModelFactory).get(MainActivityViewModel::class.java)

    }
}
확실히 Hilt라이브러리를사용해주는게 낫네요. Hilt가 없다면 뷰모델이 프로젝트에 늘어나는 만큼 중복되는 코드도 많이 늘어날 것 입니다.

 

방법2

hilt + by viewModels() 사용하기

 

이 내용은 따로 게시글을 작성하였습니다. 아래 링크를 통해서 확인 가능해요.

위에서 제일 처음 설명한 hilt를 사용해서 의존성주입을 했던 내용에 더해서 추가적으로 해야 할 작업이 있는데, 이는 사용할 객체의 구현체를 만들어두는 것입니다.

 

 

그 외의 방법

1. @AssistedInject 사용.

2. SavedStateHandle 사용.

 

 

 

참고링크

https://stackoverflow.com/questions/65280323/how-to-pass-runtime-parameters-to-a-viewmodels-constructor-when-using-hilt-for

아래에는 제가 hilt로 viewModel 객체를 사용하기 위해서 설정한 gradle 입니다.
root단계(프로젝트 단계) gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id("com.android.application") version "8.2.1" apply false
    id("org.jetbrains.kotlin.android") version "1.9.22" apply false
    id("com.google.dagger.hilt.android") version "2.50" apply false
    id("com.google.gms.google-services") version "4.3.15" apply false
}
app단계 gradle
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-kapt")

    id("dagger.hilt.android.plugin")

    id("com.google.gms.google-services")
}

...

dependencies {

    ...
 



    // Hilt dependencies
    implementation("com.google.dagger:hilt-android:2.50")
    kapt("com.google.dagger:hilt-compiler:2.50")

    ... 



}
 

 

 
 
 
 

'안드로이드' 카테고리의 다른 글

FireBaseStorageCleanArchitecutre 예제  (0) 2024.02.29
Flow (안드로이드)  (0) 2024.02.16
viewPager2 Indicator 예제  (0) 2024.02.01
DiffUtil이란?  (0) 2023.11.17
리사이클러뷰 데이터가 꼬일때 해결하는 방법  (2) 2023.11.15