4

I have injected sharedPreference in ViewModel.

Can I use android specific resource's while embedding Coroutine scope which automatically suspended when ViewModel loses scope.

I mean is it ok to use preferende in ViewModel if we add a viewModel launch scope

A CoroutineScope keeps track of all coroutines it creates. Therefore, if you cancel a scope, you cancel all coroutines it created

@ContributesViewModel
class SplashViewModel @Inject constructor(private val prefs: PrefStore) : BaseViewModel() {

    val onMoveToNext = ClassLiveData()

    init {
        scope.launch {
            val activity = if(prefs.isLoggedIn()) HomeActivity::class
            else OnBoardingActivity::class
            onMoveToNext.postValue(activity)
        }
    }

    ///fun saveDeviceID(id:String) = prefs.setDeviceID(id)
    //fun createErrorCodeHash() ={}

    fun getIsLoggedIn():Boolean = prefs.isLoggedIn()

    fun setSecondTimeLogin(boolean: Boolean) = prefs.setIsSecondTimeLogin(boolean)
}

Where

abstract class BaseViewModel: ViewModel() {

    private val job = Job()
    val scope = CoroutineScope(Dispatchers.IO + job)

    override fun onCleared() {
        super.onCleared()
        job.cancel()
    }

}

where ClassLiveData

typealias ClassLiveData = MutableLiveData<KClass<*>>

And called it in SplashActivity

viewModel.onMoveToNext.listen(this) {
    Handler().postDelayed({
         val intent = Intent(this, it.java)
        intent.putExtra("Role", "LOGIN")
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        startActivity(intent)
    }, 2000)
1
  • You can do that, but for clear separation of concerns, I would move the PrefStore in some LocalDataSource class. Look into this: github.com/android/architecture-samples Here's how the flow "should" (debatable) look like: Fragment -> ViewModel -> UseCase/Interactor/Repository -> (Remote/Local) DataSource -> Retrofit/Room/SharedPreference. Your question is more about using prefs in coroutines but I'm not sure why coroutines is even necessary to check whether the user is logged in (which basically checks a value from sharedpreferences). Commented Oct 24, 2019 at 18:12

1 Answer 1

3

As a general rule of thumb, when it comes to keeping a reference to context or other objects that have hard references on context (e.g. ContextWrapper, which is where SharedPrefferences are coming from), one should treat coroutines the same as classic threads.

For example, a live coroutine with Dispathers.IO potentially can leak the context in the same way as a classic thread, AsyncTasks, etc. Therefore, managing these references and cleaning them up is the developers' responsibility.

Looking back at your code, your base ViewModel is working on IO scope, which means any empty-constructor luanches, create a child scope on the same Dispatcher, i.e IO. However because of your ViewModel.onCleared():

override fun onCleared() {
        super.onCleared()
        job.cancel()
    }

you are pretty much safe.

Why am I saying "pretty much"?

Because it depends on how you implement your launches. Remember that only canceling a job doesn't necessarily mean that the corresponding coroutine is close and every reference is removed. Certain implementations need you directly check the state of your job inside the scope and do manual cleanups:

For instance, if you create a while loop inside a coroutine, job.cancel() does not break it, you need to break it manually.

Looking back one last time at your code, you are not doing anything inside your launch that requires manual cleanups. Then, we can say your current code certainly won't leak the context.

Sign up to request clarification or add additional context in comments.

2 Comments

I have read that business logic should be in ViewModel. And also read another place don't use android framework classes in ViewModel. So what do you suggest to pass boolean from activity to viewModel.
@xar-e-ahmer Yes in MVVM architecture, the business logic is done inside VM and also yes, in order for the VM to be unit-testable, it's better to avoid environmental variables; which in a sense, android framework classes are problematic. However, if you inject your shared preferences interface inside your VM, you can easily mock it to write your unit tests. And that should be your strategy toward pretty much everything (even non-android class), just create interfaces and inject them into your VM.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.