1일차에 설명한 바와 같이 로컬 데이터베이스를 사용해야할 일이 생겼다.
implementation("androidx.room:room-runtime:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.1")
@Module
@InstallIn(SingletonComponent::class)
class DatabaseModule {
@Provides
@Singleton
fun provideDatabase(
@ApplicationContext context: Context
)=
Room.databaseBuilder(
context,
AppDatabase::class.java,
context.getString(R.string.app_name)
).build()
@Provides
@Singleton
fun provideUserDAO(database: AppDatabase) : UserDao = database.userDao()
}
같은 데이터베이스 인스턴트를 보낼 수 있도록 싱글톤으로 설정하였다.
UserData라는 Entity를 만들었다.
@Entity
data class UserData(
@ColumnInfo(name = "user_id") val userId : Int,
@ColumnInfo(name = "user_name") val userName : String,
@ColumnInfo(name = "user_email") val email : String,
@ColumnInfo(name = "holding_dollar") val holdingDollar : Float
){
@PrimaryKey(autoGenerate = true)
var idx : Int = 0
}
primaryKey는 필요가 없어서, 일단 자동으로 설정되도록 하였다.
데이터 액세스 객체는 데이터를 삽입, 삭제, 변경할 수 있는 메서드들이 있는 객체를 말한다.
@Dao
interface UserDao {
@Insert
suspend fun insertAll(vararg userData: UserData)
// 전체 삭제
@Query("DELETE FROM USERDATA")
suspend fun deleteAll()
// 전체 데이터 개수
@Query("SELECT COUNT(user_id) FROM USERDATA")
suspend fun countAll() : Int
//user_name으로 user_email 찾기
@Query("SELECT user_email FROM userdata WHERE user_name = :userName")
suspend fun findEmailByName(userName : String) : String
//user_email
@Query("SELECT user_name FROM userdata WHERE user_email = :userEmail")
suspend fun getNameByEmail(userEmail : String) : String
}
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
viewModelScope.launch {
// 저장된 내용이 없는 경우
if (appDatabase.userDao().countAll() < 1) {
appDatabase.userDao().insertAll(
UserData(
0,
nickname!!,
email!!,
"0".toFloat()
)
)
}
// 저장된 내용이 다른 경우
else if (appDatabase.userDao().getNameByEmail(email!!) != nickname) {
UserData(
0,
nickname!!,
email!!,
"0".toFloat()
)
}
}
위와 같이 설정하고, 앱을 실행시키니 아래와 같은 에러가 떴다.
[Dagger/MissingBinding] @dagger.hilt.android.qualifiers.ActivityContext android.content.Context cannot be provided without an @Provides-annotated method.
이는 내가 LoginViewModel에서 context를 사용할 때, @ActivityContext 주석을 사용했기 때문이다. 이를 @ApplicationContext로 바꿔주면 해당 에러는 나지 않는다.
viewModel 의 생명주기가 Activity 보다 길기 때문에 Application Context 사용error: [dagger.hilt.android.processor.internal.viewmodel.ViewModelValidationPlugin]
public abstract static class SingletonC implements Application_GeneratedInjector,
Injection of an @HiltViewModel class is prohibited since it does not create a ViewModel instance correctly.
고치고 다시 실행하니 새로운 에러가 나왔다.
이는 내가 LoginActivity에서 LoginView에 LoginViewModel을 넘겨주는 방식이 잘못되었기 때문인데,
나는 원래
@Inject lateinit var viewModel: LoginViewModel
이렇게 LoginViewModel을 받아오게 했었다.
그러나
private val viewModel : LoginViewModel by viewModels()
이렇게 ViewModel을 불러와야 한다고 한다.
[참고문헌]
https://developer.android.com/training/dependency-injection/hilt-jetpack?hl=ko
데이터를 데이터베이스에 저장하는 과정에서 시간이 너무 오래 걸린다는 에러를 받았다.
이는 ViewScoped.lauch를 사용하였기 때문이다.
위의 파트를 CoroutineScope(Dispatchers.IO).lauch로 변경해주었다.
CoroutineScope(Dispatchers.IO).launch {
// 저장된 내용이 없는 경우
if (userDao.countAll() < 1) {
userDao.insertAll(
UserData(
0,
nickname!!,
email!!,
"0".toFloat()
)
)
Log.d("Room", "UserData 저장")
}
// 저장된 내용이 다른 경우
else if (userDao.findNameByEmail(email!!) != nickname) {
userDao.insertAll(
UserData(
0,
nickname!!,
email!!,
"0".toFloat()
)
)
Log.d("Room", "UserData 저장")
}
else{
Log.d("Room", "이미 저장된 내용")
}
}