IllegalArgumentException when app start post backpressed

1.6k Views Asked by At

I use "by viewModels" kotlin delegate for get some view model:

val viewModelFactory: ViewModelFactory by lazy { ViewModelFactory.getInstance(this) }

val mainViewModel: MainViewModel by viewModels { viewModelFactory }

ViewModelFactory:

class ViewModelFactory private constructor(activity: AppCompatActivity) : AbstractSavedStateViewModelFactory(activity, null) {
private val application = activity.application

// DataSource
private val appDataSource: AppDataSourceImpl by lazy { AppDataSourceImpl(application) }

// Intercator 
private val appDataInteractor: AppDataInteractor by lazy {
    AppDataInteractor(appDataSource, userDataSource)
}

companion object {
    private var instance: ViewModelFactory? = null
    fun getInstance(activity: AppCompatActivity): ViewModelFactory {
        return instance ?: ViewModelFactory(activity).also {
            instance = it
        }
    }
}

override fun <T : ViewModel> create(
    key: String,
    modelClass: Class<T>,
    handle: SavedStateHandle
): T {
    return when {
        MainViewModel::class.java.isAssignableFrom(modelClass) -> {
            modelClass.getConstructor(AppDataInteractor::class.java, SavedStateHandle::class.java)
               .newInstance(appDataInteractor, handle)
        }
        else -> throw Exception("Unknown ViewModel $modelClass")
    }
}

}

When app was closed by backpressed and launched again, it throws exception "java.lang.IllegalArgumentException: SavedStateProvider with the given key is already registered."

I see two solutions to resolve the problem. First is use exitProcess() instead of super.onBackPressed(). It is worked.

    override fun onBackPressed() {
        if (drawerLayout.isOpen) drawerLayout.close()
        else {
            if (navController.currentDestination?.id == R.id.nav_homeFragment) {
            exitProcess(1) //super.onBackPressed()
           }
        else navController.navigateUp()
     }
    }

Second is transfer my viewmodel from activity to application. In the case there are survive after destroying activity. But i can`t use "by viewModels" kotlin delegate in application. I think it is not right solution (but it work too).

Where is the right way to get viewmodels?

1

There are 1 best solutions below

0
On BEST ANSWER

The problem is with your ViewModelFactory.getInstance(this) method - it is caching the ViewModelFactory as a static instance that survives the destruction of the activity itself. Besides leaking the activity itself, it means that the second time your activity is created, the ViewModelFactory that is returned is still associated with the first, already destroyed activity.

By using by lazy, you're already ensuring that the code runs only once for the lifetime of the activity, so you should remove the instance variable and the caching within getInstance entirely.