0

I am trying a simple flow, from my activity I open the file picker then to viewmodel and this gives me a the following crash:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

And here is the code that creates this crash:

    private val mainViewModel: MainViewModel by viewModels()

    private var activityResultLaunch = registerForActivityResult(StartActivityForResult()) { result ->
        val fileUri = result.data?.data ?: return@registerForActivityResult

        val stream = contentResolver.openInputStream(fileUri) ?: return@registerForActivityResult

        uploadFiles(stream)
    }

    private fun uploadFiles(stream: InputStream) {
        lifecycleScope.launchWhenStarted {
            mainViewModel.uploadFiles(
                stream = stream
            ).asLiveData().observe(this@MainActivity, {
                handleFileUploadStatus(it)
            })
        }
    }

@HiltViewModel
class MainViewModel @Inject constructor(
    private val filesRepository: FilesRepository
) : ViewModel() {

    suspend fun uploadFiles(stream: InputStream): Flow<UploadStatusUI> {
        return filesRepository.uploadFiles(listOf(stream))
    }
}

After some research I found this issue on Google's issue tracker here and another issue on firefox's github here but nothing worked. Apparently the issue got fixed on lifecycle 2.3.1 but I am still facing it.

Here are my versions:

"androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"

"androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0"

Thanks in advance!

Edit: Crash stack trace here

Nick Pampoukidis
  • 702
  • 8
  • 28

1 Answers1

0

Per the issue you linked to, other people continue to have the same problem, so maybe there's a bug in that library. Maybe try a work around. Instead of doing all that work in the activity:

private fun uploadFiles(stream: InputStream) {
    lifecycleScope.launchWhenStarted {
        mainViewModel.uploadFiles(
            stream = stream
        ).asLiveData().observe(this@MainActivity, {
            handleFileUploadStatus(it)
        })
    }
}

Break it up:

First, a live data object in the VM:

private val _uploadStatusLiveData = MutableLiveData<UploadStatusUI>()
val uploadStatusLiveData: LiveData<UploadStatusUI> get() = _uploadStatusUI

Next change your VM suspend function to do launch the coroutine:

// Not suspend, no return type - instead collect repo flow and pass to live data
fun uploadFiles(stream: InputStream) {
    viewModelScope.launch(Dispatchers.IO) {
        filesRepository.uploadFiles(listOf(stream)).collect {
            _uploadStatusLiveData.postValue(it)
        }
    }   
}

Finally, observe the changes in activity:

fun onCreate(...) {
    viewModel.uploadStatusLiveData.observe(this) {
        handleUploadStatus(it)
    }
}

And manually start the process:

fun onStart(...) {
    viewModel.uploadFiles(stream)
}
dominicoder
  • 9,338
  • 1
  • 26
  • 32
  • Unfortunately that did not work also, the problem occurs when using the viewmodel from onActivityResult, but your answer gave me an idea for a "hacky" fix. – Nick Pampoukidis Nov 30 '21 at 16:36