Koin: Coroutines viewModelScope not working with viewModel injection

Created on 24 Jun 2019  路  9Comments  路  Source: InsertKoinIO/koin

Describe the bug
I have a basic fragment its task is to list to orders.

class MyOrdersFragment : Fragment() {
    private val orderRepository: OrderRepository by inject()
    private val viewModel: MyOrdersViewModel by viewModel { parametersOf(orderRepository) }
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        viewModel.getOrders()
        viewModel.orders.observe(this, Observer<List<Order>> {
            //... 
        })
    }
}

And I have viewmodel

class MyOrdersViewModel(private val orderRepository: OrderRepository) : ViewModel() {

    val orders = MutableLiveData<List<Order>>()

    fun getOrders() {
        viewModelScope.launch(Dispatchers.Main) {
            orderRepository.getCarts()?.let { mOrders ->
                orders.value = mOrders
            }
        }
    }

}

When I open MyOrdersFragment (first time) everything's working but when I'm close the fragment(MyOrdersFragment) then open it again not create viewmodel's instance then getOrders function is not working because "viewModelScope" canceled onCleared function of viewmodel

To Reproduce
Steps to reproduce the behavior:

  1. Open a MyOrdersFragment
  2. Run getOrders function of MyOrdersViewModel
  3. Close MyOrdersFragment
  4. MyOrdersViewModel's viewModelScope is canceled in onCleared function
  5. Open again MyOrdersFragment
  6. Run getOrders function but "viewModelScope" is not active then "orderRepository.getCarts()" not worked

Expected behavior
When I create a new fragment(like a MyOrdersFragment) it should also create a new viewmodel.

Koin project used and used version (please complete the following information):
koin-android:2.0.1
koin-androidx-viewmodel:2.0.1

Additional moduleDefinition
kotlinx-coroutines-core:1.1.1
kotlinx-coroutines-android:1.1.1
lifecycle-extensions:2.2.0-alpha01
lifecycle-viewmodel-ktx:2.2.0-alpha01

question

Most helpful comment

There is no fix yet for this. Did you manage to observe the livedata in any way yet from there?

i found solution for now such.

i'm not use "by viewModel".

private val viewModel: MyOrdersViewModel by viewModel { parametersOf(orderRepository) }

i use "getViewModel" instead of "by viewModel"

private lateinit var viewModel: MyOrdersViewModel 
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View? {

    viewModel = getViewModel<MyOrdersViewModel>()

} 

All 9 comments

There is no fix yet for this. Did you manage to observe the livedata in any way yet from there?

There is no fix yet for this. Did you manage to observe the livedata in any way yet from there?

i found solution for now such.

i'm not use "by viewModel".

private val viewModel: MyOrdersViewModel by viewModel { parametersOf(orderRepository) }

i use "getViewModel" instead of "by viewModel"

private lateinit var viewModel: MyOrdersViewModel 
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View? {

    viewModel = getViewModel<MyOrdersViewModel>()

} 

Hmm for me it is working to call the viewmodel but it doesn't want to observe the changes from the livedata. How do you actually inject it.

val module: Module = module { viewModel<ViewModel>() }

Maybe i have an issues there? And does it work for you to observe the changes from the viewmodel?

viewModel.data.observe(this, Observer { data -> data.let { Timber.d("Data Observed Size: %s", it.size) } })

i use like this

val module:Module = module{
    viewModel { MyOrdersViewModel() }
    viewModel { ProfileViewModel(get()) }
    ...
}

How did you use in viewmodel?

It is absolutely the same but at the end of the day LiveData doesn't observe the change. Here is the example chain:

Here is the viewModule

val module: Module = module { viewModel<ViewModel>() }

MainActivity:

class MainActivity: AppCompatActivity() {
private lateinit var viewModel: MainViewModel
private lateinit var binding: ActivityDeviceBinding
private lateinit var navController: NavController

override fun onCreate(savedInstanceState: Bundle?){
    super.onCreate(savedInstanceState)
    viewModel = getViewModel()
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.lifecycleOwner = this
    navController = Navigation.findNavController(this, R.id.navigation_host_fragment)
    initUI()

}

/**
 * Init UI
 */
private fun initUI() {

    /**
     * Observe the LiveData from ViewModel
     */
    viewModel.data.observe(this, Observer { data ->
        data.let {
            Timber.d("Data Observed Size: %s", it.size)
        }
    })
}

}`

Here is the ViewModel:

class MainViewModel : ViewModel(), KoinComponent {
    private val repository: Repository by inject()
    private val context: Context by inject()
    val data: MutableLiveData<List<Device>> = MutableLiveData()

    fun loadItem(){
        viewModelScope.launch(Dispatchers.Main) {
                data.postValue(listOf(Item(109, 109, "STEST", "SBEST")))
        }
    }
}

I see no issues this is sample code of the implementation and all the rest is updated to latest kotlin and koin

Even if you do it like this in the ViewModel:

class MainViewModel : ViewModel(), KoinComponent {
    private val repository: Repository by inject()
    private val context: Context by inject()
    val data: MutableLiveData<List<Device>> = MutableLiveData()

    fun loadItem(){
        data.postValue(listOf(Item(109, 109, "STEST", "SBEST")))
    }
}

It will not work at all

We experience a similar problem, also subscriber for live data doesn't handle. The problem was that we used a shared ViewModel between several screens, and for shared view model should be used different inject type.
https://insert-koin.io/docs/2.0/documentation/koin-android/index.html#_shared_viewmodel

I close this question issue. Reopen it if needed 馃憤

Hi, I'm having a similar problem with the viewmodel injection with koin and lifecycle-viewmodel.
My activity code is as follows:

class RegistryScannerActivity : ScannerActivity() {

    private val viewModel : RegistryScannerViewModel by viewModel()

     override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initObservers()
    }

    override fun onScan(result: BarcodeResult) {
        stopScanner()
        viewModel.isQRValid(result.text)
   }
}.

this is my viewmodel

class RegistryScannerViewModel(
    private val validateQrRegistryRepository: ValidateQrRegistryRepository
) : ViewModel() {

    private val _qrValidResult: MutableLiveData<Result<Boolean>> = MutableLiveData()
    val qrValidResult: LiveData<Result<Boolean>> get() = _qrValidResult

    fun isQRValid(qrValue: String) {
        coroutineScope.launch {
            _qrValidResult.value = Result.Success(validateQrRegistryRepository.isValidQr(qrValue))
        }
    }

    override fun onCleared() {
        super.onCleared()
        _qrValidResult.value = null
    }
}

The first time I run, everything works correctly, but when I return to the previous screen and return to this activity (RegistryScannerActivity) when calling method isQRValid() the corroutine does not run

Anyone know why this may be happening, I am noticing that koin generates the instances of the viewModel as singles, is this correct?

the versions of the libraries are the following:

implementation 'org.koin:koin-android-viewmodel:2.0.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

I would suggest to use the new versions since there is liveCycleViewModelScope that you can add to each observer method and also now ViewModel have its own lifeCycle for example viewModelLifecycleScope which will help a lot in this case to manage your lifecycle. Check if your views are LifeCycleAware i can't seem to see from here what's the rest of the code but it doesn't look like it is an issue with Koin. Also i believe you can change the modules not to be singleton. If you are using the viewModel in other places do refer and use this by sharedViewModel() also if you use observers use viewLifecycleOwner instead of this inside. That should fix the issues for you.

val module: Module = module { single<Repository> { RepositoryImpl() } factory { providesWorkerManager() } viewModel { DeviceListViewModel() } }

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Jeevuz picture Jeevuz  路  4Comments

hkelidari picture hkelidari  路  3Comments

erikhuizinga picture erikhuizinga  路  3Comments

CristianMG picture CristianMG  路  3Comments

AHarazim picture AHarazim  路  3Comments