https://github.com/LESSERAFIM-Aespa/TripMate/issues/126
개발을 하면서 액티비티에서 ViewModel의 함수를 사용 중,
contentViewModel.updateBudgetAndCategories(budget.copy(num = budgetNum), categories)
finish()
// ContentViewModel.kt updateBudgetAndCategories
fun updateBudgetAndCategories(budget: Budget, categories: List<Category>) =
viewModelScope.launch {
Log.d(TAG, "updateBudgetAndCategories: start")
repository.updateBudgets(budget)
val beforeCategories = budgetCategories.value.orEmpty().first().categories.orEmpty()
val startTime = budget.startDate + " 00:00"
val endTime = budget.endDate + " 23:59"
val beforeProcedures = repository.getAllProceuduresWithCategoryNums(beforeCategories.map { it.num })
.map {
when{
it.time >= endTime -> {
it.copy(time = endTime)
}
it.time <= startTime -> {
it.copy(time = startTime)
}
else -> {
it
}
}
}.toTypedArray()
repository.updateProcedures(*beforeProcedures)
val checkedArr = BooleanArray(beforeCategories.size) { false }
after@ for (category in categories) {
for ((idx, beforeCategory) in beforeCategories.withIndex()) {
if (!checkedArr[idx] && category.num == beforeCategory.num) {
if (category == beforeCategory) {
checkedArr[idx] = true
continue@after
}
repository.updateCategories(category)
checkedArr[idx] = true
continue@after
}
}
repository.insertCategories(category)
}
val etcNum = beforeCategories[2].num // 기타 카테고리
checkedArr.forEachIndexed { index, b ->
if (!b){
val currentCategory = beforeCategories[index]
val procedures = repository.getPrcedouresWithCategoryNum(currentCategory.num)
.map { it.copy(categoryNum = etcNum) }.toTypedArray()
repository.updateProcedures(*procedures)
repository.deleteCategories(currentCategory)
}
}
}
이런 식으로 ViewModelScope로 돌아가는 함수를 사용하던 중 finish로 액티비티가 먼저 꺼지게 되는 상황이 생겼습니다.
해당 상황은 삭제할 것도 많고 카테고리를 많이 추가한 상황이었습니다. 이때 액티비티가 꺼지게 되니 로그를 찍어 함수가 어디까지 진행을 하는지 찾았고 매번 끝까지 실행을 못하고 액티비티가 죽게 되니 뷰모델에서의 함수도 동작을 멈추고 죽어버렸습니다.
사실 ViewModel 같은 경우 액티비티가 죽더라도 수명주기와 독립적으로 운영되기 때문에 모든 동작은 정상작동해야 됩니다. 그렇지만 이때저는 우선 이유를 찾기보다는 동작을 우선시 해야 했기 때문에 액티비티를 모드동작을 끝날 때까지 살아있도록 코드를 작성하였습니다.
lifecycleScope.launch사용
lifecycleScope.launch {
contentViewModel.updateBudgetAndCategories(budget.copy(num = budgetNum), categories)
finish()
}
액티비티에서 이런 식으로 lifecycleScope를 사용하게 되면 viewModel의 해당작업을 모두 끝내고 액티비가 종료되기 때문에 뷰모델의 함수는 모두 돌아가게 되었습니다.
참고
https://www.charlezz.com/?p=46044
그렇지만 위에도 말했듯이 ViewModel은 액티비티와 다른 생명주기를 가지기 때문에 ViewModel이 죽는다는 것은 액티비티와 연결된 무엇인가가 있다는 말입니다. 찾아보니 ViewModel에서 사용하는 Repository가 액티비티의 Context를 사용하고 있었다는 것입니다. 그래서 액티비티가 죽는다면 Repository에서는 context를 찾을 수 없어서 ViewModel이 죽는 것처럼 보이는 것입니다. 그렇다면 우리는 Activity의 Context가아니 다른 context를 가져올 필요가 있습니다.
Application 사용
class TripMateApp : Application(){
companion object {
@Volatile
private lateinit var app: TripMateApp
@JvmStatic
fun getApp() : TripMateApp {
return app
}
}
override fun onCreate() {
app = this
super.onCreate()
}
}
class BudgetProcedureFactory(): ViewModelProvider.Factory {
private val repository by lazy {
BudgetRepositoryImpl(TripMateApp.getApp().applicationContext)
}
override fun<T : ViewModel> create(modelClass:Class<T>):T{
if(modelClass.isAssignableFrom(BudgetProcedureViewModel::class.java)) {
return BudgetProcedureViewModel(repository) as T
} else {
throw IllegalArgumentException("Not found ViewModel class.")
}
}
그래서 저는 이런 식으로 Application을 만들어 ViewModel에 사용되는 Repository에 넣어주었습니다. 이렇게 해주니 액티비티가 죽어도 ViewModel의 함수는 성공적으로 돌아갔습니다.
'안드로이드 > 안드로이드' 카테고리의 다른 글
Planfit onBoarding 페이지 클론 코딩 회고 (1) | 2024.09.13 |
---|---|
[Android/Kotlin] flatMapLatest 로 리팩토링 (2) | 2023.11.10 |
[Android/Kotlin] 알림(Notification) - 2, 알림의 속성 (1) | 2023.10.29 |
[Android/UI] BackGroundTint 값 Hex값으로 설정 (0) | 2023.10.16 |
[Android/Kotlin] Room 외래키 적용 (1) | 2023.10.14 |